<?php
include('classes/sphinxapi.php');
/* TODO: Add code here */

/**
 * Lớp đối tượng thao tác trong quá trình xây dựng sphinx kernel
 * @screen sphinxSearch.php
 * @author ThanhViet [20110121]
 */
class user_sphinx_search
{
	#region user_common
	//tên file cấu hình xml chung của sphinx
	const FILE_SPHINX_CONFIG = 'sphinx_config';
	//for Windows:
	const FOLDER_SPHINX_BIN = 'C:/sphinx/bin/';
	//for Linux:
	//const FOLDER_SPHINX_BIN = '/usr/local/bin/'; 
	
	
	//const FOLDER_FILES_SPHINX = '/var/www/html/sphinx/xml/';
	const FOLDER_FILES_SPHINX = './files/sphinx/';
	//const FOLDER_FILES_SPHINX = 'C:/sphinx/xml/';
	
	const SUFFIX_TABLE_INDEX = '_index_';
	
	const SUFFIX_TABLE_BACKUP_INDEX = '_backup_index_';
	const SUFFIX_TABLE_COUNTER = '_counter';
	
	const SUFFIX_INDEX_MAIN = '_main';
	const SUFFIX_INDEX_DELTA = '_delta';
	const SQL_CREATE_TABLE_COUNTER='DROP TABLE IF EXISTS `{0}`; CREATE TABLE `{0}` (
		`id` int(11) NOT NULL,
		`max_id` int(11) NOT NULL,
		PRIMARY KEY (`id`)
		) ENGINE=MyISAM DEFAULT CHARSET=utf8;
		';
	//Khong duoc phep khai bao const array
	//const ARRAY_DETERMINER_SPLIT  = array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
	//số lượng record cho mỗi table index								
	const NUM_RECORD_FOR_TABLE = 500000;//363445;
	//số lượng tối record cho mỗi table index
	const MAX_NUM_RECORD_FOR_TABLE = 500000;//363445; //1.000.000
	
	//số lượng chữ trước và sau đoạn match nhiều nhất để hình thành chuỗi bref result
	const DECREMENT_START_BREF = 3; 
	const INCREMENT_END_BREF = 5; 
	
	//host và port của sphinx server
	const SPHINX_HOST_NAME = 'localhost'; //localhost 192.168.1.114	
	const SPHINX_PORT_NUMBER = 3312; //3312 9306
	
	//khoảng cách thời gian chạy crontab.Thực thi command khi thời gian chênh lệch nhỏ hơn khoảng thời gian giữa 2 lần chạy
	//Nếu command cần chạy mỗi 60' thì inteval <60 để cữ mỗi lần chạy sẽ chạy đc 1 lần
	const INTERVAL_CRONTAB_RUN = 60; //60 minutes
	//thời điểm lấy mốc để tính khoảng thời gian với ngày hiện tại
	const MILESTONE_TIME = '2011-01-01 00:00:00';
	
	//dữ liệu thêm vào giữa các field
	const DATA_REDUNDANT = ' abcdef ';
	
	//user_common for action
	const ACT_SEARCH = 'search';
	
	const NUM_SEARCH_CONTEXT = 5 ;
	const ACT_CLEAR_SEARCH = 'clear_search';
	const ACT_CHANGE_PAGE = 'change';
	const ACT_NOACTION = 'noaction';
	const ACT_VERIFY_SEARCH_SECURITY_CODE ='verify_security_code';
	const OTHER_RESULT_PER_PAGE = 5 ;
	const LIMIT_SECOND_ALLOW		= 60; // Thoi gian trung binh giua 2 lan search lien tiep (tinh bang GIAY)
	const ALLOW_SEARCH_SEQUENCE		= 5;//3; // Neu search tuan tu qua so lan nay ngay lap tuc bi 1 danh dau 1 diem nghi ngo cho IP dang su dung
	const MARK_MAX_SEARCH_SEQUENCE	= 5;//3; // Neu so diem nghi ngo cua bat ky IP nao vuot qua gia tri nay thi IP do se bi BAN
	// Di nhien, van se phai xet den khoang thoi gian giua cac lan bi danh diem nghi ngo nua.
	const LIMIT_SECOND_MARK_ALLOW	= 240; // =60*4 Thoi gian trung binh giua 2 lan bi danh diem nghi ngo (tinh bang GIAY)
	
	const DESCRIPTION_SEARCH_PREFIX_PAGE			= 'Tìm ';
	const DESCRIPTION_SEARCH_SUFFIX_PAGE			= ' trong công cụ tìm kiếm câu đàm thoại song ngữ Anh Việt HelloChao.com';
	
	
	#end region
	
	#region Variables
	
	var $_objConnection;
	var $_indexName;
	//lưu array của file xml cấu hình
	var $_arrXML;
	
	var $_primaryKey;	
	
	//prefix cho talbe luc create table index
	var $_prefixTableIndex;
	
	//prefix cho talbe luc refesh data
	var $_prefixTable;
	
	//đường dẫn lưu file sphinx config	
	var $_fileSphinx;
	
	//maximum record return
	var $_iMaxRecord=1000;
	
	//number record insert per time
	var $_iBLock;
	
	//biến lưu reindex time
	var $_reindexTime;
	//biến lưu refresh data time
	var $_refreshTime;
	//biến lưu reindex meta time
	//for getItemMulti
	var $_countAll;
	var $_oldTableName;
	var $_oldStartPos;
	var $_oldEndPos;
	var $_isFirstStep = true;
	//end
	var $_reindexMetaTime;
	
	#end region
	
	#region Constructors
	/**
	 * Hàm khởi tạo đối tượng sphinxSearch
	 *
	 * @param objection $objConnection Đối tượng connection đến DB
	 * @param string $strIndexName Tên index sphinx cần thao tác
	 * @return void 
	 * @author ThanhViet [20110121]
	 *
	 */
	public function user_sphinx_search($objConnection,$strIndexName)
	{
		$this->_objConnection = $objConnection;
		$this->_indexName = $strIndexName;
		$this->parseFileConfigXML();
		$this->getPrimaryKey();
	}
	#end region
	
	#region Public methods
	
	/**
	 * Insert dữ liệu vào một record của table index
	 *
	 * @param array $arrValue mảng dữ liệu cần inset vào table index (phải đầy đủ field. Không tính increment ID)
	 * @return int Số record được insert thành công
	 * @author ThanhViet [20110121]
	 */
	public function insertNew($arrValue)
	{
		
		$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_INDEX);
		$tableName ='';
		foreach($arrTable as $item)
		{
			if (substr($item[0],strrpos($item[0],'_')+1) > substr($tableName,strrpos($tableName,'_')+1)) 
			{
				$tableName= $item[0];
			} 
		}
		//tên table là table index
		if (!$tableName) {
			$tableName = $this->_indexName.self::SUFFIX_TABLE_INDEX.'1';
		}
		
		$strMaxID = user_common::prepareQuery(user_common::SQL_SELECT,array('max(id)',$tableName));
		$arrMaxID = $this->_objConnection->selectCommand($strMaxID);
		$intMaxID = $arrMaxID?$arrMaxID[0][0]:0;
		//echo '<br>Max ID: '.$intMaxID;
		$numTable = $this->getNumofTable($tableName);
		$tableNameBackup = $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.$numTable;
		$isNewTable = 0;
		//nếu ID insert vào table index lớn hơn max record thì tạo table và insert vào table mới
		if($intMaxID > $numTable* self::MAX_NUM_RECORD_FOR_TABLE )
		{
			$tableName = user_common::cutLast($tableName,1);
			$tableName .= ($numTable+1);
			$tableNameBackup = $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.($numTable+1);
			$isNewTable = 1;
		}
		//echo '<br>NumTable: '.$numTable;
		//echo '<br>Table Name: '.$tableName;
		$startID = $this->getNewStartID($tableName);
		//tạo chuỗi SQL create table
		$strCreateSQL= $this->buildCreatTableSQL() ;
		//tạo chuỗi SQL insert into table
		$strInsertSQL = $this->buildInsertSQL();
		
		//cộng dồn dữ liệu từ các field
		
		$newValue = array_values($arrValue);
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		$index = 0;
		foreach ($arrField as $item)
		{
			if (!$item['attribute']['isOutput'] && $index < count($newValue)-1) 
			{
				$newValue[count($newValue)-1] = $newValue[$index] . self::DATA_REDUNDANT .$newValue[count($newValue)-1] ;
				$newValue[$index]='';
			}
			$index++;
		}   
		//print_r($newValue);
		
		$strSQL =  user_common::prepareQueryEscaped($strInsertSQL,array_merge((array)$tableName,$newValue));
		//echo '<br>SQL: '.$strSQL;
		//echo '<br>strCreateSQL: '.$strCreateSQL;
		
		if (!user_common::ExecutequeryWithCheckExistedTableAutoIncrement($strSQL,
			$strCreateSQL,$this->_objConnection,$tableName,$startID))
		{
			user_common::writeLog('insert index '.$this->_indexName.':'.$strSQL,1,$_mainFrame->pPage);
			return false;
		}
		//tạo chuỗi SQL insert into table backup
		$strSQLInsertBackupIndex .= user_common::prepareQueryEscaped($strInsertSQL,array_merge((array)$tableNameBackup,$arrValue));
		//echo '<br>Table Backup Name: '.$tableNameBackup;
		//echo '<br>SQL Insert Backup Index: '.$strSQLInsertBackupIndex;
		
		if (!user_common::ExecutequeryWithCheckExistedTableAutoIncrement($strSQLInsertBackupIndex,
				$strCreateSQL,$this->_objConnection,$tableNameBackup,$startID))
		{
			user_common::writeLog('insert index '.$this->_indexName.':'.$strSQL,1,$_mainFrame->pPage);
			
			$strSQL_remove = user_common::prepareQuery(user_common::SQL_DELETE_BY_CONDITION,
					array($tableName,"doc_id",$startID));
			$this->_objConnection->executeSQL($strSQL_remove);
			
			return false;
		}
		
		if($isNewTable == 1)//nếu là table mới thì build lại main index của table cuối
		{
			//echo '<br> Build main index: ';
			$this->buildMainIndexWithNum($numTable);
			user_common::writeLog('insertNew: buildMainIndexWithNum',0,$_mainFrame->pPage);
		}
		else
		{
			//nếu đã cấu hình thời gian để build meta index thì ko cần build lúc runtime
			if(!$this->_reindexMetaTime)
			{
				user_common::writeLog('insertNew: _reindexMetaTime',0,$_mainFrame->pPage);
				$this->buildMetaIndex();	
				//$this->renewCache();
			}
		}
	}
	
	/**
	 * Insert dữ liệu vào nhiều record record của table index
	 *
	 * @param array $arrValue mảng dữ liệu cần inset vào table index (phải đầy đủ field. Không tính increment ID)
	 * @param string $tableName Tên table
	 * @param int $isRefer 0:insert from main table, 1:insert from table refer
	 * @return int Số record được insert thành công
	 * @author ThanhViet [20110121]
	 */
	public function insertNewMulti($arrValues,$tableName,$isRefer)
	{
		$strInsertSQL = 'insert into `'.$tableName.'`(';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $item)
		{
			if(!$isRefer)
			{
				if ($item['children']['field_data'][0]['value']) 
				{
					$strInsertSQL .='`'.$item['attribute']['name'].'`,';
				}
			}
			else
			{
				if ($item['children']['field_refer'][0]['children']['field_data'][0]['value']) 
				{
					$strInsertSQL .='`'.$item['attribute']['name'].'`,';
				}
			}
		}
		$strInsertSQL = user_common::cutLast($strInsertSQL,1);
		$strInsertSQL .= ') values';
		
		foreach($arrValues as $item)
		{
			$strInsertSQL.= '(';
			$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
			foreach ($arrField as $field)
			{
				if(!$isRefer)
				{
					if ($field['children']['field_data'][0]['value']) 
					{
						$strInsertSQL.= '\''.user_common::escape_mysql_string(
								$item[$field['children']['field_data'][0]['value']]).'\',';
					}
				}
				else
				{
					if ($field['children']['field_refer'][0]['children']['field_data'][0]['value']) 
					{
						$strInsertSQL.= '\''.user_common::escape_mysql_string(
								$item[$field['children']['field_refer'][0]['children']['field_data'][0]['value']]).'\',';
					}
				}
			}
			$strInsertSQL = user_common::cutLast($strInsertSQL,1);
			$strInsertSQL.= '),';
		}
		$strInsertSQL = user_common::cutLast($strInsertSQL,1);
		
		//$strSQL =  user_common::prepareQueryEscaped($strInsertSQL,array_merge((array)$tableName,$arrValue));
		$strCreateSQL= $this->buildCreatTableSQL() ;
		$startID = $this->getNewStartID($tableName);
		//echo '<br>create table:'.$strCreateSQL;
		//echo '<br><br><br><br><br><br>insert into:'.$strInsertSQL;
		//$tableName = $this->_indexName.'_index';
		if (!user_common::ExecutequeryWithCheckExistedTableAutoIncrement(
					$strInsertSQL,$strCreateSQL,$this->_objConnection,$tableName,$startID))
		{
			user_common::writeLog('insert new multi index '.$this->_indexName.':'.$strInsertSQL,1);
			return false;
		}	
		
	}
	
	/**
	 * Buid index chỉ với đoạn dữ liệu meta
	 *
	 * @return void
	 * @author ThanhViet [20110121]
	 */
	public function buildMetaIndex()
	{
		user_common::writeLog('buildMetaIndex:<p>Thực thi rebuild meta index<p>',0,$_mainFrame->pPage);
		//echo '<p>Thực thi rebuild meta index<p>';
		
		$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_INDEX);
		$strIndex = 'index_meta_'.$this->_indexName;
		
		//$arrSphinx = Application::xml2array(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG);
		$arrSphinx = Application::getVarXML(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG,'sphinx');
		
		$pathData = $arrSphinx["children"]["index"][0]["attribute"]["path"].$item[0];
		//lấy tên các index tương ứng
		//echo $pathData.'meta_'.$this->_indexName.'.sph';
		$rotate = file_exists($pathData.'meta_'.$this->_indexName.'.sph')?'--rotate':'';
		$strCommand= self::FOLDER_SPHINX_BIN.'indexer '.$rotate.' --config '.$this->_fileSphinx .' '.$strIndex; 
		//echo '<BR>run command: '.$strCommand.'<br>';
		user_common::writeLog('buildMetaIndex:<BR>run command: '.$strCommand.'<br>',0,$_mainFrame->pPage);
		user_common::writeLog('buildMetaIndex:'.shell_exec($strCommand),0,$_mainFrame->pPage);
		user_common::writeLog('buildMetaIndex:<p>Hoàn thành rebuild meta index<p>',0,$_mainFrame->pPage);
		
		//echo system($strCommand);  
		//echo '<p>Hoàn thành rebuild meta index<p>';
		
		// 		$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_INDEX);
		// 		$strIndex = 'index_meta_'.$this->_indexName;
		// 		
		// 		$file = fopen('start_server.bat', 'w'); 
		// 		fwrite($file, 'cd /d '.self::FOLDER_SPHINX_BIN.' 
		// 					indexer --rotate --config '.$this->_fileSphinx .' '.$strIndex.'	
		// 					pause
		// 					'); 
		// 		fclose($file); 
		// 		echo system('start_server.bat');  
	}
	
	/**
	 * Build index toàn bộ dữ liệu + build sphinx config
	 *
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	public function buildMainIndex()
	{
		//echo '<p>Thực thi rebuild main index<p>';
		//xoa dữ liệu trong table index cũ để cập nhật dữ liệu mới (dữ liệu các field có isOutput=0 là rỗng)
		$this->clearTableIndex(self::SUFFIX_TABLE_INDEX);
		//chuyển dữ liệu từ table backup index để fill các field có isOutput = 0
		$this->tranferDataFromBackupTable();
		//tạo index trong file cấu hình
		$this->buildSphinxConfig();
		//build index bằng command
		$this->runCommandConfig();
		//xóa lại các field có isOutput=0 để tiết kiệm thời gian search lấy content_id
		//$this->clearContentTableIndex(self::SUFFIX_TABLE_INDEX);
		//echo '<p>Hoàn thành rebuild main index<p>';
		user_common::writeLog('buildMainIndex:<p>Hoan thanh rebuild main index<p>',0,$_mainFrame->pPage);
		
		
	}
	
	/**
	 * Build index toàn bộ dữ liệu + build sphinx config nhưng chỉ 1 index
	 * Khi insert new record mà vượt max record của table
	 *
	 * @param $numTable Số cuối cùng của table trước khi phân table mới khi insert 1 record.
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	public function buildMainIndexWithNum($numTable)
	{
		echo '<p>Thực thi rebuild main index with num table ',$numTable,' <p>';
		//xóa dữ liệu trong table index cũ hiện tại để cập nhật dữ liệu mới (dữ liệu các field có isOutput=0 là rỗng)
		$this->clearOneTableIndex(self::SUFFIX_TABLE_INDEX, $numTable);
		//chuyển dữ liệu từ table backup index để fill các field có isOutput = 0 ở table có giá trị index là $numTable
		$this->tranferDataFromOneBackupTable($numTable);
		//tạo index trong file cấu hình
		$this->buildSphinxConfig();
		//build index bằng command nhưng chỉ với index chỉ định giá trị $numTable
		$this->runCommandConfigWithNumTable(array($numTable,$numTable+1));
		//xóa lại các field có isOutput=0 để tiết kiệm thời gian search lấy content_id
		$this->clearContentOneTableIndex(self::SUFFIX_TABLE_INDEX, $numTable);
		//echo '<p>Thực thi rebuild index with num table ',$numTable,' <p>';
		user_common::writeLog('buildMainIndexWithNum:<p>Thực thi rebuild index with num table ',$numTable,' <p>',0,$_mainFrame->pPage);
		
	}
	
	/**
	 * This is method renewCache
	 *
	 * @return mixed This is the return value description
	 *
	 */
	function renewCache()
	{
		//$strCommand = "#!/bin/bash \n";
		//$strCommand = "sudo \n";
		//$strCommand .= self::FOLDER_SPHINX_BIN.'searchd  --config '.
		//					$this->_fileSphinx ." --stop \n";
		$strCommand .= self::FOLDER_SPHINX_BIN.'searchd  --config '.$this->_fileSphinx. " --stop \n " ;
		$strCommand .= self::FOLDER_SPHINX_BIN.'searchd  --config '.$this->_fileSphinx ;
		//user_common::writeToFile(getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh',$strCommand);
		user_common::writeLog('<BR>run renew cache command: '.$strCommand.'<br>',0,$_mainFrame->pPage);
		//echo system('bash '.getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh');
		//user_common::writeLog('<BR>run renew cache command: '.shell_exec($strCommand),0,$_mainFrame->pPage);
		user_common::writeLog('<BR>run renew cache command: '.shell_exec($strCommand),0,$_mainFrame->pPage);
		user_common::writeLog('renewCache:<p>run command: '.$strCommand.'<p>',0,$_mainFrame->pPage);
	}
	
	/**
	 *Run command to build index
	 *
	 * @return void 
	 *
	 */
	public function runCommandConfig() 
	{
		//lấy tất cả tên các table của table index
		$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_INDEX);
		//$strIndexs = '';
		$strCommand = '';
		//$arrSphinx = Application::xml2array(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG);
		$arrSphinx = Application::getVarXML(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG,'sphinx');
		
		$pathData = $arrSphinx["children"]["index"][0]["attribute"]["path"].$item[0];
		
		//for Linux
		/*
		$strCommand = "#!/bin/bash \n";
		//$strCommand = "sudo \n";
		//lấy tên các index tương ứng
		foreach($arrTable as $table)
		{
			$rotate = file_exists($pathData.$table[0].'.sph')?'--rotate':'';
			//echo $table[0].'.sph';
			//$strIndexs .= 'index_'.$table[0].' ';
			$strCommand .='sudo '. self::FOLDER_SPHINX_BIN.'indexer '.$rotate.' --config '.
							$this->_fileSphinx .' index_'.$table[0].' ';
			$strCommand .="\n";
		}
	
		//tạo câu lệnh để rebuild index bằng command line
		echo '<BR>run command: '.$strCommand.'<br>';
		user_common::writeToFile(getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh',$strCommand);
		echo system('chmod 777 '.getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh');
		echo '<br>bash '.getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh<br>';
		echo shell_exec('sudo bash '.getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh');
		user_common::writeLog('runCommandConfig:<p>run command: '.$strCommand.'<p>',0,$_mainFrame->pPage);
		*/
		
		//user_common::writeLog('runCommandConfig: '.shell_exec('./'.getcwd().'/'.user_common::FOLDER_FILES_OTHER.'main.sh'),0,$_mainFrame->pPage);
		//user_common::writeLog('runCommandConfig: '.system($strCommand),0,$_mainFrame->pPage);
		//system('searchd --config '.$this->_fileSphinx .' --stop');
		//system('searchd --config '.$this->_fileSphinx .' ');
		//echo system($strCommand);  
		//exec($strCommand);  
		
		
		//for Windows
		$strCommand = "";
		//$strCommand = "sudo \n";
		//lấy tên các index tương ứng
		foreach($arrTable as $table)
		{
			$rotate = file_exists($pathData.$table[0].'.sph')?'--rotate':'';
			//echo $table[0].'.sph';
			//$strIndexs .= 'index_'.$table[0].' ';
			$strCommand = self::FOLDER_SPHINX_BIN.'indexer '.$rotate.' --config '.
				$this->_fileSphinx .' index_'.$table[0].' ';
			//echo '<BR>run command: '.$strCommand.'<br>';
			//echo shell_exec($strCommand);
			user_common::writeLog('runCommandConfig: run command: '.$strCommand,0,$_mainFrame->pPage);;
			user_common::writeLog('runCommandConfig: '.shell_exec($strCommand),0,$_mainFrame->pPage);;
		}
	}
	
	/**
	 *Run command to build index nhưng chỉ 1 index
	 *
	 * @param $arrNumTable Mảng chứa các số cuối của index
	 * @return void 
	 *
	 */
	public function runCommandConfigWithNumTable($arrNumTable) 
	{
		//$strIndexs = '';
		foreach($arrNumTable as $item)
		{
			//$strIndexs .= 'index_'.$this->_indexName.self::SUFFIX_TABLE_INDEX.$item.' ';
			//$rotate = file_exists($this->_indexName.self::SUFFIX_TABLE_INDEX.$item.'.sph')?'--rotate':'';
			$strCommand .= self::FOLDER_SPHINX_BIN.'indexer  --config '.
					$this->_fileSphinx .'index_'.$this->_indexName.self::SUFFIX_TABLE_INDEX.$item.' ';
		
		}
		//$strCommand= self::FOLDER_SPHINX_BIN.'indexer --rotate --config '.$this->_fileSphinx .' '.$strIndexs; 
		
		echo system($strCommand);  
		//exec($strCommand);  
		// 		$strIndexs = '';
		// 		foreach($arrNumTable as $item)
		// 		{
		// 			$strIndexs .= 'index_'.$this->_indexName.self::SUFFIX_TABLE_INDEX.$item.' ';
		// 		}
		// 		$file = fopen('start_server.bat', 'w'); 
		// 		fwrite($file, 'cd /d '.self::FOLDER_SPHINX_BIN.' 
		// 					indexer --rotate --config '.$this->_fileSphinx .' '.$strIndexs.'	
		// 					pause
		// 					'); 
		// 		fclose($file); 
		// 		echo system('start_server.bat');  
	}
	
	/**
	 * Xây dựng các cấu hình để index trong file sphinx config
	 *
	 * @return void 
	 * @author ThanhViet [20110126]
	 */
	private function buildSphinxConfig()
	{
		$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_INDEX);
		if(count($arrTable)>0)
		{
			//$arrSphinx = Application::xml2array(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG);
			$arrSphinx = Application::getVarXML(self::FOLDER_FILES_SPHINX.self::FILE_SPHINX_CONFIG,'sphinx');
			
			$strSphinx="";
			$arrSource = $arrSphinx["children"]["source"][0]["children"]["para"];
			$arrIndex = $arrSphinx["children"]["index"][0]["children"]["para"];
			//start of index
			$strSphinx .="#start $this->_indexName\n";
			
			//lấy table có số id auto increment lớn nhất để sử dụng cho trường hợp live index
			$lastTableName ="";
			foreach($arrTable as $item)
			{
				if (substr($item[0],strrpos($item[0],"_")+1) > substr($lastTableName,strrpos($lastTableName,"_")+1)) 
				{
					$lastTableName= $item[0];
				} 
			}
			//end
			
			//lấy các field của table index
			$strSelectField ="";
			//lấy các field cần sort hay group (attribute)
			$strAttrib = "";
			$arrField = $this->_arrXML["children"]["table"][0]["children"]["field"];
			$strSelectField .= "`id`,";
			//print_r($arrField);
			foreach ($arrField as $field)
			{
				$strSelectField .="`".$field["attribute"]["name"]."`,";
				
				if(isset($field["attribute"]["order"]) || isset($field["attribute"]["filter"]))
				{
					$strType = substr($field["attribute"]["datatype"],strlen($field["attribute"]["datatype"])-3);	
					switch($strType) 
					{
						//chuoi
						case "ext"://text
						case "har"://varchar,char
							$strAttrib .="\t";
							$strAttrib .="sql_attr_str2ordinal";
							$strAttrib .= " = ";
							$strAttrib .=$field["attribute"]["name"];
							$strAttrib .="\n";
							break;
						case "int":
							$strAttrib .="\t";
							$strAttrib .="sql_attr_uint";
							$strAttrib .= " = ";
							$strAttrib .=$field["attribute"]["name"];
							$strAttrib .="\n";
							break;
						default:
					}
				}
				
				
			}
			$strSelectField = user_common::cutLast($strSelectField,1);
			//end
			
			foreach($arrTable as $item)
			{
				$strSphinx .="source src_$item[0] \n";
				$strSphinx .="{\n";
				foreach($arrSource as $source)
				{
					$strSphinx .="\t";
					$strSphinx .=$source["attribute"]["name"];
					$strSphinx .= " = ";
					$strSphinx .=$source["attribute"]["value"];
					$strSphinx .="\n";
				}
				
				
				//nếu là table index chứa ID auto lớn nhất thì thêm sql_pre max_id
				if($item[0] == $lastTableName)
				{
					//add sql pre max_id
					$strSphinx .="\t";
					$strSphinx .="sql_query_pre";
					$strSphinx .= " = ";
					$strSphinx .="REPLACE INTO ".$this->_indexName.self::SUFFIX_TABLE_COUNTER." SELECT 1, MAX(id) FROM $lastTableName";
					$strSphinx .="\n";
				}
				//add sql query
				$strSphinx .="\t";
				$strSphinx .="sql_query";
				$strSphinx .= " = ";
				$strSphinx .="SELECT  $strSelectField FROM $item[0] ";
				$strSphinx .="\n";
				
				//nếu có field cần sort
				if($strAttrib)
				{
					$strSphinx .= $strAttrib;
				}
				
				//add sql info
				$strSphinx .="\t";
				$strSphinx .="sql_query_info";
				$strSphinx .= " = ";
				$strSphinx .="SELECT  $strSelectField FROM ".$item[0]." where id=\$id";
				$strSphinx .="\n";
				
				$strSphinx .="}\n";
				
				$strSphinx .="index index_$item[0] \n";
				//add source
				$strSphinx .="{\n";
				$strSphinx .="\t";
				$strSphinx .="source";
				$strSphinx .= " = ";
				$strSphinx .="src_$item[0]";
				$strSphinx .="\n";
				//add path
				$strSphinx .="\t";
				$strSphinx .="path";
				$strSphinx .= " = ";
				$strSphinx .=$arrSphinx["children"]["index"][0]["attribute"]["path"].$item[0];
				$strSphinx .="\n";
				
				foreach($arrIndex as $index)
				{
					$strSphinx .="\t";
					$strSphinx .=$index["attribute"]["name"];
					$strSphinx .= " = ";
					if ($index["attribute"]["name"] == "charset_table") 
					{
						$arrCharset = explode("\\",$index["attribute"]["value"] );
						foreach($arrCharset as $charSet)
						{
							$charSet = $charSet."\ \n";
							$charSet = str_replace(" ","",$charSet);
							$strSphinx .="\t \t".$charSet;
						}
						$strSphinx = user_common::cutLast($strSphinx,2);
					}
					else
					{
						$strSphinx .=$index["attribute"]["value"];
					}
					
					//echo "<br>".$index["attribute"]["value"];
					$strSphinx .="\n";
				}
				$strSphinx .="}\n";
			}
			
			//start set index meta
			$strSphinx .="source src_meta_$this->_indexName: src_$lastTableName \n";
			$strSphinx .="{\n";
			//add sql query
			$strSphinx .="\t";
			$strSphinx .="sql_query_pre";
			$strSphinx .= " = ";
			$strSphinx .="SET NAMES utf8";
			$strSphinx .="\n";
			//add sql info
			$strSphinx .="\t";
			$strSphinx .="sql_query";
			$strSphinx .= " = ";
			$strSphinx .="SELECT $strSelectField FROM $lastTableName WHERE id> (SELECT max_id FROM ".$this->_indexName.self::SUFFIX_TABLE_COUNTER." WHERE id=1)";
			$strSphinx .="\n";
			
			$strSphinx .="}\n";
			
			$strSphinx .="index index_meta_$this->_indexName: index_$lastTableName \n";
			$strSphinx .="{\n";
			//add source			
			$strSphinx .="\t";
			$strSphinx .="source";
			$strSphinx .= " = ";
			$strSphinx .="src_meta_$this->_indexName";
			$strSphinx .="\n";
			//add path
			$strSphinx .="\t";
			$strSphinx .="path";
			$strSphinx .= " = ";
			$strSphinx .=$arrSphinx["children"]["index"][0]["attribute"]["path"]."meta_".$this->_indexName ;
			$strSphinx .="\n";
			
			$strSphinx .="}\n";
			//end set index meta
			
			//start set distribute index
			$strSphinx .="index dis_".$this->_indexName."\n";
			$strSphinx .="{\n";
			//add type
			$strSphinx .="\t";
			$strSphinx .="type";
			$strSphinx .= " = ";
			$strSphinx .="distributed";
			$strSphinx .="\n";
			//add local
			$strSphinx .="\t";
			$strSphinx .="local";
			$strSphinx .= " = ";
			$strSphinx .="index_meta_".$this->_indexName;
			$strSphinx .="\n";
			//add local
			foreach($arrTable as $item)
			{
				$strSphinx .="\t";
				$strSphinx .="local";
				$strSphinx .= " = ";
				$strSphinx .="index_$item[0]";
				$strSphinx .="\n";
			}			
			//add agent_connect_timeout
			$strSphinx .="\t";
			$strSphinx .="agent_connect_timeout";
			$strSphinx .= " = ";
			$strSphinx .="1000";
			$strSphinx .="\n";
			//add agent_query_timeout
			$strSphinx .="\t";
			$strSphinx .="agent_query_timeout";
			$strSphinx .= " = ";
			$strSphinx .="3000";
			$strSphinx .="\n";
			$strSphinx .="}\n";
			//end set distribute index
			
			//end of index
			$strSphinx .="#end $this->_indexName\n";
			
			$strOutput = "";
			
			$lines = file($this->_fileSphinx);
			$isSave= true;
			$strOutput .= $strSphinx;
			foreach($lines as $line)
			{
				if (trim($line) == "#start $this->_indexName") 
				{
					$isSave = false;	
				}
				if ($isSave) 
				{
					$strOutput.=$line;
					//echo("<br>".$line);
				}
				if (trim($line) == "#end $this->_indexName") 
				{
					$isSave = true;
				}
			}
			//echo ini_get('open_basedir'); 
			//echo $this->_fileSphinx.'<br>';
			//echo $strOutput;
			user_common::writeToFile($this->_fileSphinx,$strOutput);
			//user_common::writeToFile('/var/www/vhosts/test.abc',$strOutput);
		}
	}
	
	/**
	 *Chuyển tất cả các record table backup index sang table indexs
	 *
	 * @return int -1: not successfull ;>=0 successfull
	 * @author ThanhViet [20110121]
	 */
	public function tranferDataFromBackupTable()
	{
		//$this->getAllTableIndex()
		//$strSQL = 'SELECT table_name FROM information_schema.TABLES WHERE table_name LIKE ''.$this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.'%';';
		//$arrDropTable = $this->_objConnection->selectCommand($strSQL);
		$arrDropTable = $this->getAllTableIndex(self::SUFFIX_TABLE_BACKUP_INDEX);
		//print_r($arrDropTable);
		$strSQL='';
		$strSQL .= user_common::prepareQuery(self::SQL_CREATE_TABLE_COUNTER,array($this->_indexName.self::SUFFIX_TABLE_COUNTER));
		$strCreate = $this->buildCreatTableSQL();
		
		$selectConcatField = '';
		$selectField = '';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $item)
		{
			if (!$item['attribute']['isOutput']) 
			{
				$selectConcatField .='`'.$item['attribute']['name'].'`,\''. self::DATA_REDUNDANT .'\',';
				$selectField.= '\'\',';
			}
			else
			{
				$selectField.= '`'.$item['attribute']['name'].'`,';
			}
		}   
		$selectConcatField = user_common::cutLast($selectConcatField,1);
		$selectConcatField = 'CONCAT('.$selectConcatField.')';
		
		$selectField = user_common::cutLast($selectField,3);
		
		$selectField = '`id`,'.$selectField.$selectConcatField;
		
		if (count($arrDropTable)>0) 
		{
			foreach($arrDropTable as $item)
			{
				$tableName = $this->_indexName.self::SUFFIX_TABLE_INDEX.$this->getNumofTable($item[0]);				
				$strCreateTable = user_common::prepareQuery($strCreate,array($tableName,1));
				$strSQL .=$strCreateTable.' insert into '.$tableName.' select '.$selectField.' from '. $item[0].';';
			}
		}
		//echo '<br />sql:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	/**
	 *Chuyển tất cả các record table backup index sang table indexs
	 *
	 * @return int -1: not successfull ;>=0 successfull
	 * @author ThanhViet [20110121]
	 */
	public function tranferDataFromOneBackupTable($numTable)
	{
		$tableNameIndex= $this->_indexName.self::SUFFIX_TABLE_INDEX.$numTable;	
		$tableBackupIndex= $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.$numTable;	
		$strCreateTable = $this->buildCreatTableSQL();
		$strCreateTable = user_common::prepareQuery($strCreateTable,array($tableNameIndex,1));
		$strSQL .=$strCreateTable.' insert into '.$tableNameIndex.' select * from '. $tableBackupIndex.';';
		//echo '<br />sql:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	/**
	 * Refesh data of table backup index
	 *
	 * @return void 
	 *
	 */
	public function refeshDataFromBackupTable()
	{
		//khong co du lieu thi tra ve null
		if(!$this->_arrXML)
		{
			return null;	
		}
		//echo '<p>Thực thi refresh data<p>';
		//xoa du lieu hien co trong table backup index
		$this->clearTableIndex(self::SUFFIX_TABLE_BACKUP_INDEX);
		
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		$isExternalData = 0;
		foreach ($arrField as $field)
		{
			if ($field['children']['field_data'][0]['value'] && $field['children']['file'][0]['value']) 
			{
				$isExternalData = 1;
			}
		}
		if ($isExternalData) //lay data len roi moi insert
		{
			//insert from main table
			$this->setValueFromTable();
		}
		else //chuyen du lieu truc tiep trong db
		{
			$this->setValueFromTableDirect();
		}
		//insert from table refer
		if(isset($this->_arrXML['children']['table'][0]['children']['table_refer']))
		{
			$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
			$isExternalData = 0;								
			foreach ($arrField as $field)
			{
				if ($field['children']['field_refer'][0]['children']['field_data'][0]['value'] && $field['children']['field_refer'][0]['children']['file'][0]['value']) 
				{
					$isExternalData = 1;
				}
			}
			if ($isExternalData) //ley data len roi moi insert
			{
				//insert from main table
				$this->setValueFromTableRefer();
			}
			else //chuyen du lieu truc tiep trong db
			{
				$this->setValueFromTableDirectRefer();
			}
		}
		//echo '<p>Hoàn thành refresh data<p>';
	}
	
	/**
	 * Clear data of table backup index
	 * @param string $suffixTable suffix table can clear _index_ ; _backup_index_
	 * @return int -1: not successfull ;>=0 successfull
	 *
	 */
	private function clearTableIndex($suffixTable) 
	{
		//$arrDropTable = $this->getAllTableIndex($suffixTable);
		//kết hợp query tablename và tạo câu lệnh drop table
		$strSQL = 'SELECT CONCAT(\'DROP TABLE `\', table_name,\'`;\') FROM information_schema.TABLES WHERE table_name LIKE \''.$this->_indexName.$suffixTable.'%\';';
		$arrDropTable = $this->_objConnection->selectCommand($strSQL);
		
		$strSQL='';
		if (count($arrDropTable)>0) 
		{
			foreach($arrDropTable as $item)
			{
				$strSQL .= $item[0];
			}
		}
		//echo '<br/>drop table:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	/**
	 * Xóa nội dung các field có isOutput=0
	 *
	 * @param string $suffixTable suffix table can clear _index_ ; _backup_index_
	 * @return int -1: not successfull ;>=0 successfull
	 *
	 */
	private function clearContentTableIndex($suffixTable) 
	{
		//$arrDropTable = $this->getAllTableIndex($suffixTable);
		//kết hợp query tablename và tạo câu lệnh update table
		$strSQL = 'SELECT CONCAT(\'UPDATE `\', table_name,\'` SET `vn_content`=\\\'\\\';\') FROM information_schema.TABLES WHERE table_name LIKE \''.$this->_indexName.$suffixTable.'%\';';
		$arrDropTable = $this->_objConnection->selectCommand($strSQL);
		
		$strSQL='';
		if (count($arrDropTable)>0) 
		{
			foreach($arrDropTable as $item)
			{
				$strSQL .= $item[0];
			}
		}
		//echo '<br/>update table:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	/**
	 * Clear data of the fields of table with NumTable
	 *
	 * @param string $suffixTable suffix table can clear _index_ ; _backup_index_
	 * @param int $numTable Suffix table can clear
	 * @return int -1: not successfull ;>=0 successfull
	 *
	 */
	private function clearContentOneTableIndex($suffixTable,$numTable) 
	{
		//$arrDropTable = $this->getAllTableIndex($suffixTable);
		//kết hợp query tablename và tạo câu lệnh update table
		$strSQL = 'UPDATE `'.$this->_indexName.$suffixTable.$numTable.'` SET `vn_content`=\'\';';
		//echo '<br/>update table:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	/**
	 * Clear data of table backup index
	 *
	 * @param string $suffixTable suffix table can clear _index_ ; _backup_index_
	 * @param int $numTable Suffix table can clear
	 * @return int -1: not successfull ;>=0 successfull
	 *
	 */
	private function clearOneTableIndex($suffixTable,$numTable) 
	{
		$strSQL = 'DROP TABLE '.$this->_indexName.$suffixTable.$numTable.';';
		//echo '<br/>drop table:'.$strSQL;
		return $this->_objConnection->executeMultiSQL($strSQL);
	}
	
	//goi tu buildMainIndex
	//insertField: field insert trong table index
	/**
	 * Chuyển dữ liệu từ main table  vào table index
	 *
	 * @param array $insertField Các field insert vào table index tương ứng với table chính
	 * @param string $selectField Các field cần lấy để inset vào table index
	 * @param string $whereClause Câu cần filter trong table refer
	 * @param string $prefixTable Refix của table chính
	 * @param string $typePaging Loại paging của table refer : month hay alpabet
	 * @return void
	 * @author ThanhViet [20110121]
	 */
	
	/**
	 * Chuyển dữ liệu từ main table  vào table index
	 *
	 * @return void
	 * @author ThanhViet [20110125]
	 *
	 */
	public function setValueFromTable()
	{
		$this->_prefixTable = $this->_arrXML['children']['table'][0]['attribute']['name'];;
		
		$selectField = '';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $item)
		{
			if ($item['children']['field_data'][0]['value']) 
			{
				$selectField .='`'.$item['children']['field_data'][0]['value'] .'`,';
			}
		}
		$selectField = user_common::cutLast($selectField, 1);
		
		//kiểm tra xem table có được paging không và paging theo month hay theo alphabet
		$typePagingTable = $this->_arrXML['children']['table'][0]['attribute']['prefix_by'];
		if($typePagingTable && $this->_prefixTable)
		{
			switch($typePagingTable) 
			{
				case 'month':
					$numMonth = $this->_arrXML['children']['table'][0]['attribute']['num_month'];
					$my = user_common::getDateTime();
					$tmpID = user_common::buildIDByMonth('0',$my,$numMonth,user_common::CLIENT_TABLE);
					$strTableName = user_common::builtTableName($this->_prefixTable,user_common::getTableSuffixByMonth($tmpID));
					break;
				case 'alphabet':
					$strTableName = $this->_prefixTable.'__';
					break;
			}
		}
		else
		{
			$strTableName = $this->_indexName;
		}
		$intPage = 1;
		// 
		do {
			// Reset public value to call function getArticleMulti
			$whereClause =  $this->_arrXML['children']['table'][0]['attribute']['where_clause'];
			
			$arrResultMulti = array();	
			
			if($typePagingTable)
			{
				$this->_countAll = 0;
				$this->_oldTableName = '';
				$this->_oldStartPos = 0;
				$this->_oldEndPos = 0;
				
				switch($typePagingTable) 
				{
					case 'month':
						$numMonth = $this->_arrXML['children']['table'][0]['attribute']['num_month'];
						$this->_isFirstStep = true;	
						$this->getItemMultiByMonth($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$numMonth,$whereClause);
						break;
					case 'alphabet':
						$this->getItemMultiByAlphabet($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
						break;
				}
			}
			else//table ko có paging
			{
				$this->getItemMultiByNone($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
			}	
			print_r($arrResultMulti);
			
			$strSQL = '';
			if (count($arrResultMulti)>0)
			{				
				$arrMain = array();
				foreach($arrResultMulti as $item)
				{
					if ($whereClause) 
					{
						$strSQL .= '('.user_common::prepareQuery(user_common::SQL_SELECT_FREE,array($selectField,$item['0'],
									' WHERE '.$whereClause.'  limit '.$item['1'].','.$item['2'])).') UNION ALL ';
					}
					else
					{
						$strSQL .= '('.user_common::prepareQuery(user_common::SQL_SELECT_FREE,array($selectField,$item['0'],
									' limit '.$item['1'].','.$item['2'])).') UNION ALL ';
					}
				}
				if (strlen($strSQL)>0)
				{
					$strSQL = substr($strSQL,0,strlen($strSQL)-11);	
				}
				// tạo câu truy vấn lấy thông tin article theo article_id
				$arrMain= $this->_objConnection->selectCommand('select * from ('.$strSQL.') as cmulti ');
				$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
				$lenMain = count($arrMain);
				for($i=0;$i<$lenMain;$i++)
				{
					foreach ($arrField as $field)
					{
						if ($field['children']['field_data'][0]['value']) 
						{
							if ($field['children']['file'][0]['value']) 
							{
								//get prefix folder
								$strFolder = $field['children']['file'][0]['attribute']['prefix_folder'];
								$typePaging = $field['children']['file'][0]['attribute']['paging'];
								//get pagging folder
								switch($typePaging) 
								{
									case 'time':
										$strFolder.= '20'.substr($arrMain[$i][$this->_primaryKey],0,user_common::PREFIX_BY_MONTHS-2).'/';
										break;
									case 'alphabet':
										break;
								}
								//get suffix folder
								if ($field['children']['file'][0]['attribute']['suffix_folder']) 
								{
									$strFolder.=$field['children']['file'][0]['attribute']['suffix_folder'];
								}
								$arrMain[$i][$field['children']['field_data'][0]['value']] = 
									user_common::readFromFile(
										$strFolder.$arrMain[$i][$field['children']['field_data'][0]['value']]);
							}
						}
					}
				}
				$tableName = $this->getNewTableName($intPage);
				//echo '<br>Start from:'.$intPage*$this->_iBLock;
				$this->insertNewMulti($arrMain,$tableName,0);
				//echo '<br>Finish from:'.$intPage*$this->_iBLock;
				$intPage += 1;
				
			}
		//nếu không lấy thêm được kết quả từ những table thì thoát ra
		} while(count($arrResultMulti)>0);			
	}
	
	/**
	 * Chuyển dữ liệu từ main table  vào table index
	 *
	 * @return void
	 * @author ThanhViet [20110125]
	 *
	 */
	public function setValueFromTableDirect()
	{
		$this->_prefixTable = $this->_arrXML['children']['table'][0]['attribute']['name'];;
		$selectField = '';
		//dung co cau insert from table
		$strInsertField = '';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $field)
		{
			if ($field['children']['field_data'][0]['value']) 
			{
				$selectField .='`'.$field['children']['field_data'][0]['value'] .'`,';
				$strInsertField .='`'.$field['attribute']['name'].'`,';
			}
			
		}
		$strInsertField = user_common::cutLast($strInsertField, 1);
		$selectField = user_common::cutLast($selectField, 1);
		
		//kiểm tra xem table có được paging không và paging theo month hay theo alphabet
		$typePagingTable = $this->_arrXML['children']['table'][0]['attribute']['prefix_by'];
		if($typePagingTable && $this->_prefixTable)
		{
			switch($typePagingTable) 
			{
				case 'month':
					$numMonth = $this->_arrXML['children']['table'][0]['attribute']['num_month'];
					$my = user_common::getDateTime();
					$tmpID = user_common::buildIDByMonth('0',$my,$numMonth,user_common::CLIENT_TABLE);
					$strTableName = user_common::builtTableName($this->_prefixTable,user_common::getTableSuffixByMonth($tmpID));
					break;
				case 'alphabet':
					$strTableName = $this->_prefixTable.'__';
					break;
			}
		}
		else
		{
			$strTableName = $this->_prefixTable;
		}
		$intPage = 1;
		// 
		do {
			
			// Reset public value to call function getArticleMulti
			$arrResultMulti = array();
			$whereClause =  $this->_arrXML['children']['table'][0]['attribute']['where_clause'];
			
			if($typePagingTable)
			{
				$this->_countAll = 0;
				$this->_oldTableName = '';
				$this->_oldStartPos = 0;
				$this->_oldEndPos = 0;
				
				switch($typePagingTable) 
				{
					case 'month':
						$numMonth = $this->_arrXML['children']['table'][0]['attribute']['num_month'];
						$this->_isFirstStep = true;	
						$this->getItemMultiByMonth($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$numMonth,$whereClause);
						break;
					case 'alphabet':
						$this->getItemMultiByAlphabet($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
						break;
				}
			}
			else//table ko có paging
			{
				$this->getItemMultiByNone($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
			}	
			//print_r($arrResultMulti);
			
			$strSQL = '';
			if (count($arrResultMulti)>0)
			{
				$arrMain = array();
				
				$tableName = $this->getNewTableName($intPage);
				
				foreach($arrResultMulti as $item)
				{
					if ($whereClause) 
					{
						$strSQL .= 'insert into '.$tableName.'('.$strInsertField.') 
								(select '.$selectField.' from '.$item['0'].' WHERE '.$whereClause.'  limit '.$item['1'].','.$item['2'].');' ;
						$strSQL .= 'ALTER TABLE '.$tableName.' AUTO_INCREMENT = 1;' ;
					}
					else
					{
						$strSQL .= 'insert into '.$tableName.'('.$strInsertField.') 
								(select '.$selectField.' from '.$item['0'].' limit '.$item['1'].','.$item['2'].');' ;
						$strSQL .= 'ALTER TABLE '.$tableName.' AUTO_INCREMENT = 1;' ;
					}
				}
				// 				if (strlen($strSQL)>0)
				// 				{
				// 					$strSQL = substr($strSQL,0,strlen($strSQL)-11);	
				// 				}
				// tạo câu truy vấn lấy thông tin article theo article_id
				//echo '<br><br>SQL: '.$strSQL;
				
				//$strSQL =  user_common::prepareQueryEscaped($strInsertSQL,array_merge((array)$tableName,$arrValue));
				$strCreateSQL= $this->buildCreatTableSQL() ;
				//echo '<br>Create Table SQL: '.$strCreateSQL;
				//lay so bat dau auto ID increment cua table moi
				$startID = $this->getNewStartID($tableName);
				if (!user_common::ExecutequeryWithCheckExistedTableAutoIncrement(
							$strSQL,$strCreateSQL,$this->_objConnection,$tableName,$startID))
				{
					user_common::writeLog('setValueFromTableDirect index '.$this->_indexName.':'.$strInsertSQL,1);
					//return false;
				}	
				
				//echo '<br>Start from:'.$intPage*$this->_iBLock;
				//$this->insertNewMulti($arrMain,$tableName,0);
				//echo '<br>Finish from:'.$intPage*$this->_iBLock;
				$intPage += 1;
			}
			//break;
		} while(count($arrResultMulti)>0 );			
	}
	
	/**
	 * Chuyển dữ liệu từ table refer vào table index với chuỗi ID cho trước
	 *
	 * @return void
	 * @author ThanhViet [20110121]
	 */
	public function setValueFromTableRefer()
	{
		$selectField = '';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $item)
		{
			if ($item['children']['field_refer'][0]['children']['field_data'][0]['value']) 
			{
				$selectField .='`'.$item['children']['field_refer'][0]['children']['field_data'][0]['value'] .'`,';
			}
		}
		$selectField = user_common::cutLast($selectField, 1);
		
		$prefixTable = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['name'];
		if($prefixTable) //có table refer
		{
			$whereClause =  $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['where_clause'];
			
			$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_BACKUP_INDEX);
			$tableName ='';
			foreach($arrTable as $item)
			{
				if (substr($item[0],strrpos($item[0],'_')+1) > substr($tableName,strrpos($tableName,'_')+1)) 
				{
					$tableName= $item[0];
				} 
			}
			if (!$tableName) 
			{
				$tableName = $this->_indexName.'_index_1';
			}
			//lấy table cuối cùng của phần table được tạo từ setValueFromTable
			$numTable = substr($tableName,strrpos($tableName,'_')+1);
			//kiểm tra xem table có được paging không và paging theo month hay theo alphabet
			$typePagingTable = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['prefix_by'];
			if($typePagingTable)
			{
				switch($typePagingTable) 
				{
					case 'month':
						$numMonth = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['num_month'];
						$my = user_common::getDateTime();
						$tmpID = user_common::buildIDByMonth('0',$my,$numMonth,$this->_indexName);
						$strTableName = user_common::builtTableName($prefixTable,user_common::getTableSuffixByMonth($tmpID));
						break;
					case 'alphabet':
						$strTableName = $this->_indexName.'__';
						break;
				}
			}
			else
			{
				$strTableName = $this->_indexName;
			}
			
			$intPage = 1;
			do {
				$this->_prefixTable = $prefixTable;
				// Reset public value to call function getArticleMulti
				$arrResultMulti = array();
				//$whereClause =  $this->_arrXML['children']['table'][0]['attribute']['where_clause'];
				
				if($typePagingTable)
				{
					$this->_countAll = 0;
					$this->_oldTableName = '';
					$this->_oldStartPos = 0;
					$this->_oldEndPos = 0;
					
					switch($typePagingTable) 
					{
						case 'month':
							$this->_isFirstStep = true;		
							$this->getItemMultiByMonth($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$numMonth,$whereClause);
							break;
						case 'alphabet':
							$this->getItemMultiByAlphabet($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
							break;
					}
				}
				else//table ko có paging
				{
					$this->getItemMultiByNone($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
				}	
				//print_r($arrResultMulti);
				
				$strSQL = '';
				if (count($arrResultMulti)>0)
				{
					$arrMain = array();
					foreach($arrResultMulti as $item)
					{
						if ($whereClause) {
							$strSQL .= '('.user_common::prepareQuery(user_common::SQL_SELECT_FREE,
									array($selectField,$item['0'],'where  '.$whereClause.' limit '.$item['1'].','.$item['2'])).') UNION ALL ';
						}
						else
						{
							$strSQL .= '('.user_common::prepareQuery(user_common::SQL_SELECT_FREE,
									array($selectField,$item['0'],'limit '.$item['1'].','.$item['2'])).') UNION ALL ';
						}
					}
					if (strlen($strSQL)>0)
					{
						$strSQL = substr($strSQL,0,strlen($strSQL)-11);	
					}
					
					//echo '<br>Table Refer:' .$strSQL;
					// tạo câu truy vấn lấy thông tin article theo article_id
					$arrMain= $this->_objConnection->selectCommand('select * from ('.$strSQL.') as cmulti ');
					$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
					$lenMain = count($arrMain);
					for($i=0;$i<$lenMain;$i++)
					{
						foreach ($arrField as $field)
						{
							
							if ($field['children']['field_refer'][0]['children']['field_data'][0]['value']) 
							{
								
								if ($field['children']['field_refer'][0]['children']['file'][0]['value']) 
								{
									//get prefix folder
									$strFolder = $field['children']['field_refer'][0]['children']['file'][0]['attribute']['prefix_folder'];
									$typePaging = $field['children']['field_refer'][0]['children']['file'][0]['attribute']['paging'];
									//get pagging folder
									switch($typePaging) 
									{
										case 'time':
											$strFolder.= '20'.substr($arrMain[$i][$this->_primaryKey],0,user_common::PREFIX_BY_MONTHS-2).'/';
											break;
										case 'alphabet':
											break;
									}
									//get suffix folder
									if ($field['children']['field_refer'][0]['children']['file'][0]['attribute']['suffix_folder']) 
									{
										$strFolder.=$field['children']['field_refer'][0]['children']['file'][0]['attribute']['suffix_folder'];
									}
									
									
									$arrMain[$i][$field['children']['field_refer'][0]['children']['field_data'][0]['value']] = 
										user_common::readFromFile($strFolder.$arrMain[$i][$field['children']['field_data'][0]['value']]);
								}
							}
							
						}
					}
					
					$tableName = $this->getNewTableName($intPage);					
					$this->insertNewMulti($arrMain,$tableName,1);
					
					//echo '<br>Start from:'.$intPage*$this->_iBLock;
					//echo '<br>Finish from:'.$intPage*$this->_iBLock;
					$intPage += 1;
				}
			} while(count($arrResultMulti)>0);
		}
	}
	
	/**
	 * Chuyển dữ liệu từ table refer vào table index với chuỗi ID cho trước . Chuyen du lieu truc tiep trong db
	 *
	 * @return void
	 * @author ThanhViet [20110121]
	 */
	public function setValueFromTableDirectRefer()
	{
		$selectField = '';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		//dung cho cau truy van insert from table
		$strInsertField = '';
		foreach ($arrField as $field)
		{
			if ($field['children']['field_refer'][0]['children']['field_data'][0]['value']) 
			{
				$selectField .='`'.$field['children']['field_refer'][0]['children']['field_data'][0]['value'] .'`,';
				$strInsertField .='`'.$field['attribute']['name'].'`,';
			}
		}
		$selectField = user_common::cutLast($selectField, 1);
		$strInsertField = user_common::cutLast($strInsertField, 1);
		
		//lấy prefix table refer
		$prefixTable = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['name'];
		if($prefixTable) //có table refer
		{
			$whereClause =  $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['where_clause'];
			//láy danh sách các table refer
			$arrTable = $this->getAllTableIndex(self::SUFFIX_TABLE_BACKUP_INDEX);
			$tableName ='';
			foreach($arrTable as $item)
			{
				if (substr($item[0],strrpos($item[0],'_')+1) > substr($tableName,strrpos($tableName,'_')+1)) 
				{
					$tableName= $item[0];
				} 
			}
			if (!$tableName) 
			{
				$tableName = $this->_indexName.'_index_1';
			}
			//lấy table cuối cùng của phần table được tạo từ setValueFromTable
			$numTable = substr($tableName,strrpos($tableName,'_')+1);
			//kiểm tra xem table có được paging không và paging theo month hay theo alphabet
			$typePagingTable = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['prefix_by'];
			if($typePagingTable)
			{
				switch($typePagingTable) 
				{
					case 'month':
						$numMonth = $this->_arrXML['children']['table'][0]['children']['table_refer'][0]['attribute']['num_month'];
						$my = user_common::getDateTime();
						$tmpID = user_common::buildIDByMonth('0',$my,$numMonth,$this->_indexName);
						$strTableName = user_common::builtTableName($prefixTable,user_common::getTableSuffixByMonth($tmpID));
						break;
					case 'alphabet':
						$strTableName = $this->_indexName.'__';
						break;
				}
			}
			else //không có paging
			{
				$strTableName = $this->_indexName;
			}
			
			$intPage = 1;
			do {
				$this->_prefixTable = $prefixTable;
				// Reset public value to call function getArticleMulti
				$arrResultMulti = array();
				//$whereClause =  $this->_arrXML['children']['table'][0]['attribute']['where_clause'];
				
				if($typePagingTable)
				{
					$this->_countAll = 0;
					$this->_oldTableName = '';
					$this->_oldStartPos = 0;
					$this->_oldEndPos = 0;
					
					switch($typePagingTable) 
					{
						case 'month':
							$this->_isFirstStep = true;		
							$this->getItemMultiByMonth($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$numMonth,$whereClause);
							break;
						case 'alphabet':
							$this->getItemMultiByAlphabet($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
							break;
					}
				}
				else//table ko có paging
				{
					$this->getItemMultiByNone($intPage,$arrResultMulti, $strTableName,$this->_iBLock,$whereClause);
				}	
				//print_r($arrResultMulti);
				
				$strSQL = '';
				//chuyển dữ liệu 
				if (count($arrResultMulti)>0)
				{
					$arrMain = array();
					$tableName = $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.(FLOOR(($intPage*$this->_iBLock)/self::NUM_RECORD_FOR_TABLE)+1+$numTable);
					//tạo các câu multi insert
					foreach($arrResultMulti as $item)
					{
						if ($whereClause) 
						{
							$strSQL .= 'insert into '.$tableName.'('.$strInsertField.') 
									(select '.$selectField.' from '.$item['0'].' WHERE '.$whereClause.'  limit '.$item['1'].','.$item['2'].');' ;
							$strSQL .= 'ALTER TABLE '.$tableName.' AUTO_INCREMENT = 1;' ;
						}
						else
						{
							$strSQL .= 'insert into '.$tableName.'('.$strInsertField.') 
									(select '.$selectField.' from '.$item['0'].' limit '.$item['1'].','.$item['2'].');' ;
							$strSQL .= 'ALTER TABLE '.$tableName.' AUTO_INCREMENT = 1;' ;
						}
					}
					
					//echo '<br>SQL: '.$strSQL;
					
					$strCreateSQL= $this->buildCreatTableSQL() ;
					//echo '<br>Create Table SQL: '.$strCreateSQL;
					//lấy giá trị bắt đầu của 1 table
					$startID = $this->getNewStartID($tableName);
					if (!user_common::ExecutequeryWithCheckExistedTableAutoIncrement(
								$strSQL,$strCreateSQL,$this->_objConnection,$tableName,$startID))
					{
						user_common::writeLog('setValueFromTableDirectRefer index '.$this->_indexName.' :'.$strInsertSQL,1);
						//return false;
					}	
					//echo '<br>Start from:'.$intPage*$this->_iBLock;
					//echo '<br>Finish from:'.$intPage*$this->_iBLock;
					$intPage += 1;
				}
			} while(count($arrResultMulti)>0);
		}
	}
	
	/**
	 * Lấy các record theo tháng
	 *
	 * @param int $intPage Chi so trang
	 * @param array $resultMulti Mảng trả về
	 * @param string $strTableName Tên table
	 * @param int $numRecord Số lượng record cần lấy
	 * @param string $whereClause Điều kiện filter
	 * @return void 
	 * @author ThanhViet [20110121]
	 *
	 */
	private function getItemMultiByMonth($intPage,&$resultMulti,$strTableName,$numRecord,$numMonth,$whereClause)
	{
		//build câu select count để tính số record cần lấy
		if ($whereClause) 
		{
			$strCountAll= user_common::prepareQuery(user_common::SQL_SELECT_FREE,
					array('count(*)',$strTableName,'where  '.$whereClause));
		}
		else
		{
			$strCountAll = user_common::prepareQuery(user_common::SQL_SELECT,array('count(*)',$strTableName));
		}
		$arrCountAll = $this->_objConnection->selectCommand($strCountAll);
		$intCountMain = $arrCountAll?$arrCountAll[0][0]:0;
		//cộng dồn tổng số record với tổng số record của table trước đó
		$this->_countAll += $intCountMain;
		//tình toán chỉ số record bắt đầu và số lượng record kết thúc
		$startPos = ($intPage - 1)*$numRecord;
		$endPos = $startPos + $numRecord;		
		if ($startPos >= $this->_countAll)  // Tong so rescord chua cham trang can lay
		{
			if (!$this->_isFirstStep && $intCountMain==0) // Khong phai buoc dau tien, thoat. Tranh bi loop forever
			{
				return;
			}
		}
		elseif ($endPos <= $this->_countAll)
		{ 
			// Da co du lieu can thiet
			if ($this->_oldTableName){
				$resultMulti[$this->_oldTableName] = 
					array('0'=>$this->_oldTableName,'1'=>$this->_oldStartPos,'2'=>($this->_oldEndPos - $this->_oldStartPos));
				$resultMulti[$strTableName] = array('0'=>$strTableName,'1'=>0,'2'=>($endPos - ($this->_countAll - $intCountMain)));
			}else{
				$resultMulti[$strTableName] = 
					array('0'=>$strTableName,'1'=>($startPos-($this->_countAll - $intCountMain)), $numRecord);//'2'=>($startPos-($this->_countAll - $intCountMain) + $numRecord));
			}
			return;
		}
		else
		{ // ($startPos < $this->_countAll) && ($this->_countAll < $endPos)
			if ($this->_oldTableName)
			{
				$resultMulti[$this->_oldTableName] = 
					array('0'=>$this->_oldTableName,'1'=>$this->_oldStartPos,'2'=>($this->_oldEndPos - $this->_oldStartPos));				
				if ($intCountMain>0)
				{
					$resultMulti[$strTableName] = array('0'=>$strTableName,'1'=>0,'2'=>$intCountMain);
				}
				return;
			}
			else
			{ 
				// Cache it
				$this->_oldTableName = $strTableName;
				$this->_oldStartPos = $startPos - ($this->_countAll - $intCountMain);
				$this->_oldEndPos = $intCountMain;
			}	
		}
		
		if ($this->_isFirstStep){
			$this->_isFirstStep = false;
		}
		
		// Goi de quy
		// dem so record cua thang truoc
		//TODO xem lai num month suffix
		$strTableComment1 = $this->_prefixTable.user_common::SYS_TABLE.user_common::monthPlus(
				substr($strTableName,strlen($strTableName)-user_common::PREFIX_BY_MONTHS),(-1)*$numMonth);
		self::getItemMultiByMonth($intPage,&$resultMulti, $strTableComment1, $numRecord,$numMonth,$whereClause);
	}
	
	/**
	 * Lấy các record theo alphabet
	 *
	 * @param int $intPage Chi so trang
	 * @param array $resultMulti Mảng trả về
	 * @param string $strTableName Tên table
	 * @param int $numRecord Số lượng record cần lấy
	 * @param string $whereClause Điều kiện filter
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	private function getItemMultiByAlphabet($intPage,&$resultMulti,$strTableName,$numRecord,$whereClause)
	{
		//build câu select count
		if ($whereClause) 
		{
			$strCountAll= user_common::prepareQuery(user_common::SQL_SELECT_FREE,
					array('count(*)',$strTableName,'where  '.$whereClause));
		}
		else
		{
			$strCountAll = user_common::prepareQuery(user_common::SQL_SELECT,array('count(*)',$strTableName));
		}
		$arrCountAll = $this->_objConnection->selectCommand($strCountAll);
		
		$intCountMain = $arrCountAll?$arrCountAll[0][0]:0;
		//cộng dồn tổng số record với tổng số record của table trước đó
		$this->_countAll += $intCountMain;
		//tình toán chỉ số record bắt đầu và số lượng record kết thúc
		$startPos = ($intPage - 1)*$numRecord;
		$endPos = $startPos + $numRecord;		
		
		if ($startPos < $this->_countAll)  // Tong so rescord dat duoc số cần lấy
		{
			if ($endPos <= $this->_countAll)
			{ 
				// Da co du lieu can thiet
				if ($this->_oldTableName)
				{
					$resultMulti[$this->_oldTableName] = 
						array('0'=>$this->_oldTableName,'1'=>$this->_oldStartPos,'2'=>($this->_oldEndPos - $this->_oldStartPos));
					$resultMulti[$strTableName] = array('0'=>$strTableName,'1'=>0,'2'=>($endPos - ($this->_countAll - $intCountMain)));
				}
				else
				{
					$resultMulti[$strTableName] = 
						array('0'=>$strTableName,'1'=>($startPos-($this->_countAll - $intCountMain)), $numRecord);//'2'=>($startPos-($this->_countAll - $intCountMain) + $numRecord));
				}
				return;
			}
			else
			{ // ($startPos < $this->_countAll) && ($this->_countAll < $endPos)
				if ($this->_oldTableName)
				{
					$resultMulti[$this->_oldTableName] = 
						array('0'=>$this->_oldTableName,'1'=>$this->_oldStartPos,'2'=>($this->_oldEndPos - $this->_oldStartPos));				
					if ($intCountMain>0)
					{
						$resultMulti[$strTableName] = array('0'=>$strTableName,'1'=>0,'2'=>$intCountMain);
					}
				}
				else
				{ // Cache it
					if ($intCountMain>0) //luu lai table khi co du lieu
					{
						$this->_oldTableName = $strTableName;
						$this->_oldStartPos = $startPos - ($this->_countAll - $intCountMain);
						$this->_oldEndPos = $intCountMain;
					}
					else
					{
						$this->_oldTableName = '';
					}
				}	
			}
		}
		
		$strChar = substr($strTableName,strlen($strTableName)-user_common::PREFIX_BY_ALPHABET);
		if (strtolower($strChar) == 'z')
		{
			return;
		}
		
		// Goi de quy
		// dem so record cua thang truoc
		$strTableComment1 = $this->_prefixTable.user_common::SYS_TABLE.user_common::alphabetPlus(substr
				($strTableName,strlen($strTableName)-user_common::PREFIX_BY_ALPHABET));
		self::getItemMultiByAlphabet($intPage,&$resultMulti, $strTableComment1, $numRecord,$whereClause);
	}
	
	/**
	 * Lấy các record ko theo alphabet cũng ko theo tháng
	 *
	 * @param int $intPage Chi so trang
	 * @param array $resultMulti Mảng trả về
	 * @param string $strTableName Tên table
	 * @param int $numRecord Số lượng record cần lấy
	 * @param string $whereClause Điều kiện filter
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	private function getItemMultiByNone($intPage,&$resultMulti,$strTableName,$numRecord,$whereClause)
	{
		//buid câu SQL select count
		if ($whereClause) //có điều kiện where clause
		{
			$strCountAll= user_common::prepareQuery(user_common::SQL_SELECT_FREE,
					array('count(*)',$strTableName,'where  '.$whereClause));
		}
		else
		{
			$strCountAll = user_common::prepareQuery(user_common::SQL_SELECT,array('count(*)',$strTableName));
		}
		
		$arrCountAll = $this->_objConnection->selectCommand($strCountAll);
		//tổng record
		$intCountMain = $arrCountAll?$arrCountAll[0][0]:0;
		//Start record
		$startPos = ($intPage - 1)*$numRecord;
		//End record
		$endPos = $startPos + $numRecord;	
		if ($startPos >= $intCountMain)  // Tong so rescord chua cham trang can lay
		{
			if (!$this->_isFirstStep) // Khong phai buoc dau tien, thoat. Tranh bi loop forever
			{
				return;
			}
		}
		if ($endPos <=$intCountMain) //End record nhỏ hơn tổng số record
		{
			$resultMulti[$strTableName] = 
				array('0'=>$strTableName,'1'=>$startPos, $numRecord);
		}
		else
		{
			//lấy hết record
			$resultMulti[$strTableName] = 
				array('0'=>$strTableName,'1'=>'0', $numRecord);
		}
		if ($this->_isFirstStep){
			$this->_isFirstStep = false;
		}
		return;	
	}
	
	/**
	 * Lấy danh sách các primary key dựa vào file cấu hình
	 *
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	private function getPrimaryKey()
	{
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		foreach ($arrField as $field)
		{
			if ($field['attribute']['isPrimary']) 
			{
				$this->_primaryKey = $field['children']['field_data'][0]['value'];
				return;
			}
		}
	}
	
	/**
	 * Tạo tên table dựa vào số phân trang
	 *
	 * @param int $intPage Số trang
	 * @return string Tên table
	 * @author ThanhViet [20110121]
	 */
	private function getNewTableName($intPage) 
	{
		//tên table index chính là tên indexname
		if ( ($intPage*$this->_iBLock) % self::NUM_RECORD_FOR_TABLE )
		{
			$tableName = $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.(FLOOR(($intPage*$this->_iBLock)/self::NUM_RECORD_FOR_TABLE)+1);
		}
		else
		{
			$tableName = $this->_indexName.self::SUFFIX_TABLE_BACKUP_INDEX.(($intPage*$this->_iBLock)/self::NUM_RECORD_FOR_TABLE);
		}
		return $tableName;
	}
	
	/**
	 * Lấy start id của table index tương ứng với index của table
	 *
	 * @param string $tableName Tên table
	 * @return int Chỉ số index
	 * @author ThanhViet [20110121]
	 */
	private function getNewStartID($tableName) 
	{
		return  ($this->getNumofTable($tableName)-1)*self::MAX_NUM_RECORD_FOR_TABLE + 1;
	}
	
	/**
	 * Phương thức tô đậm chuỗi match với key search
	 *
	 * @param string $strMsg Chuối cần search
	 * @param string $strKey Key word search
	 * @return string Kết quả trả về chuỗi đã tô đậm phần match và 2 vị trí index bắt đầu và kết thúc chuỗi match
	 * @author ThanhViet [20110121]
	 */
	public function highlightResult($strMsg,$strKey,$showAll=0)
	{
		//$strMsg = strtolower($strMsg);
		//$strKey = strtolower($strKey);
		//vị trí match đầu tiên của input
		$iStart =-1;
		//vị trí match cuối cùng của input
		$iEnd = -1;
		//vị trí match nhiều nhất đầu tiên của input
		$iStartMax =-1;
		//vị trí match nhiều nhất cuối cùng của input
		$iEndMax = -1;
		
		//lưu số chữ match nhiều nhất để xử lý hiển thị đoạn match
		$icountMax=0;
		//lưu tạm số chữ match
		$iCount = 0;
		$search  = array(',','?','.',';',':','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
		$replace = array(' , ',' ? ',' . ',' ; ',' : ',' " ',' - ',' = ',' ! ',' ( ',' ) ',' [ ',' ] ',' { ',' } ',' | ',' + ',' \\ ',' * ');
		$strMsg = str_replace($search, $replace, $strMsg);
		$arrDeterminer= array(' ',',','?','.',';',':','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
		//$arrDeterminer= array(' ',',',';',':','"','-','=','(',')','[',']','{','}','|','+','\\','*');
		
		//$arrInput = user_common::explodeX(array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*'),$strMsg);
		//có thể lấy luôn dấu chấm , phẩy
		$arrInput = user_common::explodeX(array(' '),$strMsg);
		$arrKey = user_common::explodeX($arrDeterminer,$strKey);
		//$arrKey = user_common::explodeX(array(' '),$strKey);
		//print_r($arrKey);
		$isUnicode = 0;
		if (user_common::checkUnicodeString($strKey) )
		{
			$isUnicode = 1;
		}
		$lenInput = count($arrInput);
		$lenKey = count($arrKey);
		//echo $isUnicode;
		for($i=0 ;$i<$lenInput;$i++)	
		{
			$iMatch=-1;
			$iCount = 0 ;
			//kiểm tra chỉ trùng có đoạn giữa
			$isIncrease = 0;
			for($j=0 ;$j<$lenKey;$j++)
			{
				if ($isUnicode) 
				{
					//loại bỏ ký tự đặc biệt khi so sánh
					$input1 = str_replace($arrDeterminer,'',$arrInput[$i]) ;
					$input2 = $arrKey[$j];
					//tranh trường hợp truyền vô dấu nháy đầu tiên
					if($input2[0]=='\'')
					{
						$input2= str_replace('\'','',$input2);	
					}
					if($input2[strlen($input2)-1]=='\'')
					{
						$input2=user_common::cutLast($input2,1);
					}
				}
				else
				{
					//loại bỏ ký tự đặc biệt khi so sánh
					$input1 = user_common::utf8_to_ascii(str_replace($arrDeterminer,'',$arrInput[$i]));
					$input2 = user_common::utf8_to_ascii($arrKey[$j]);
					//tranh trường hợp truyền vô dấu nháy đầu tiên
					if($input2[0]=='\'')
					{
						$input2= str_replace('\'','',$input2);	
					}
					if($input2[strlen($input2)-1]=='\'')
					{
						$input2=user_common::cutLast($input2,1);
					}
					
				}
				if (mb_strtolower($input1,"UTF-8") ==mb_strtolower($input2,"UTF-8") ) 
				{
					//echo '<br>$input1: '.$input1.'<br>$input2: '.$input2.'<br>';
					if ($iMatch == -1) 
					{
						$arrInput[$i] = '<b>'.$arrInput[$i];
						//lưu lai vị đầu tiên của chuỗi match
						$iStart = $i;
					}
					
					$iMatch = $j;
					//kiểm tra từ ở vị trí tiếp theo
					$i++;
					$isIncrease = 1;
					$iCount ++;
				}
				else
				{
					//kiểm tra có phải là vị trí trùng cuối cùng 
					if ($iMatch != -1 && $iMatch == $j-1) 
					{
						$arrInput[$i-1] = $arrInput[$i-1].'</b>';
						//lưu lai vị cuối cùng của chuỗi match
						$iEnd = $i-1;
						//$i= $i-1;
					}
					$iMatch = -1;
				}
			}
			//trùng nguyên đoạn cần search
			if ($iMatch != -1)
			{
				$arrInput[$i-1] = $arrInput[$i-1].'</b>';
				//lưu lai vị cuối cùng của chuỗi match
				$iEnd = $i-1;
				$iMatch = -1;
				$i--;
			}	
			//giảm lại một index nếu chỉ trùng có 1 đoạn giữa
			if ($iMatch == -1 && $isIncrease ==1)
			{
				$i--;
			}
			//lưu lại vj trí trùng nhiều nhất
			if($iCount > $icountMax)
			{
				$icountMax = $iCount;
				$iStartMax = $iStart;
				$iEndMax = $iEnd;	
			}
		} 
		$arrContentBold = array();
		//lấy đầu câu từ đoạn trùng
		for($i = $iStartMax ;$i>=0;$i--)
		{
			if ((($iStartMax-$i)>40) || !$showAll && ($arrInput[$i] == '.'||$arrInput[$i] == '?'||$arrInput[$i] == '!')) 
			{
				break;
			}	
			
			//không lấy dấu đầu câu.
			array_unshift($arrContentBold,$arrInput[$i]);
		}
		//$iStartMax = $i;
		
		//lấy cuối câu trừ đoạn trùng
		for($i = $iStartMax+1 ;$i< $lenInput;$i++)
		{
			//lấy luôn dấu cuối câu.
			array_push($arrContentBold,$arrInput[$i]);
			//phải lấy qua đoạn bold.Nếu cần lấy hết không quan tâm dấu chấm câu
			if ( (($i - $iStartMax)>40) || !$showAll && $i > $iEndMax  &&  ($arrInput[$i] == '.'||$arrInput[$i] == '?'||$arrInput[$i] == '!'||$arrInput[$i] == ';') ) 
			{
				break;
			}	
		}
		
		$strResult = implode(' ', $arrContentBold);
		//cắt bỏ khoẳng trắng sau các dấu
		$search  = array('" ',' "','\' ',' \'','( ',' )','[ ',' ]','{ ',' }',' ?',' .',' !',' ,');
		$replace  = array('"','"','\'','\'','(',')','[',']','{','}','?','.','!',',');
		$strResult= str_replace($search,$replace,$strResult);
		$lenResult = strlen($strResult);
		//dau hieu co ky tu "
		$flag1 = 0;
		//dau hieu co ky tu '
		$flag2 = 0;
		for($i=0;$i<$lenResult;$i++)
		{
			//nếu trước dấu " là ký tự alphabet thì thêm 1 khoảng trắng
			if ($strResult[$i]=='"' && $flag1 == 0)
			{
				//có dấu " thêm khoảng trắng phía trước "
				$flag1 = 1;
				$step = 1;
				if ($strResult[$i-$step] == '>') {
					$step = 5;
				}
				//kiểm tra ký tự
				if (ord(strtolower($strResult[$i-$step])) >=97 && ord(strtolower($strResult[$i-$step])) <=122) 
				{
					//insert khoảng trắng
					$strResult = substr_replace($strResult,' ', $i-$step+1, 0);
					//$strResult[$i] ='&nbsp' ;
					//$strResult[$i+$step] = $temp;
				}
				//qua đoạn có ký tự "
				$i++;
				$lenResult = strlen($strResult);
				continue;
			}	
			
			if ($strResult[$i]=='"' && $flag1 == 1)
			{
				//có dấu " thêm khoảng trắng phía sau "
				$flag1 = 0;
				$step = 1;
				if ($strResult[$i+$step] == '<') {
					$step = 4;
				}
				//kiểm tra ký tự
				if (ord(strtolower($strResult[$i+$step])) >=97 && ord(strtolower($strResult[$i+$step])) <=122) 
				{
					//insert khoảng trắng
					$strResult = substr_replace($strResult,' ', $i+$step, 0);
					//$strResult[$i] ='&nbsp' ;
					//$strResult[$i+$step] = $temp;
				}
				$lenResult = strlen($strResult);
				$i++;
				continue;
			}
			// xử lý thêm khoẳng trắng trước và sau dấu '
			if ($strResult[$i]=='\'' && $flag2 == 0) 
			{
				//có dấu ' thêm khoảng trắng phía trước '
				$flag2 = 1;
				$step = 1;
				if ($strResult[$i-$step] == '>') {
					$step = 5;
				}
				//kiểm tra ký tự
				if (ord(strtolower($strResult[$i-$step])) >=97 && ord(strtolower($strResult[$i-$step])) <=122) 
				{
					//insert khoảng trắng
					$strResult = substr_replace($strResult,' ', $i-$step+1, 0);
					//$strResult[$i] ='&nbsp' ;
					//$strResult[$i+$step] = $temp;
				}
				//qua đoạn có ký tự "
				$i++;
				$lenResult = strlen($strResult);
				continue;	
			}
			if ($strResult[$i]=='\'' && $flag2 == 1)
			{
				//có dấu ' thêm khoảng trắng phía sau '
				$flag2 = 0;
				$step = 1;
				if ($strResult[$i+$step] == '<') {
					$step = 4;
				}
				//kiểm tra ký tự
				if (ord(strtolower($strResult[$i+$step])) >=97 && ord(strtolower($strResult[$i+$step])) <=122) 
				{
					//insert khoảng trắng
					$strResult = substr_replace($strResult,' ', $i+$step, 0);
					//$strResult[$i] ='&nbsp' ;
					//$strResult[$i+$step] = $temp;
				}
				$lenResult = strlen($strResult);
				$i++;
				continue;
			}
		}
		//echo 'Key: '.$strKey;
		//echo '<br>';
		
		//echo '<br>';
		//echo '<br>';
		//echo 'Vị trí match nhiều nhất là -> '.$iStartMax.' : '.$iEndMax;
		//$strOutput2 = substr($strResult,$iStartMax,$iEndMax-$iStartMax+40);
		//echo '<br>Bref output: '.$strOutput2;//.'<br>';
		//echo '<br>';
		//echo '<br>';
		
		//tính phần trăm matching
		$percentMathing = floor(($iEndMax-$iStartMax+1)*100/count($arrKey));
		
		$arrReturn = array('content'=>$strResult,'matching'=>$percentMathing,'start'=>$iStartMax,'end'=>$iEndMax);
		//print_r($arrReturn);
		//echo '<br>';
		return $arrReturn;	
	}
	
	/**
	 * Phương thức search với Sphinx cho module cặp câu
	 *
	 * @param string $strKey Chuỗi cần search
	 * @param mixed $numPage Số thứ tụ trang hiện tại
	 * @param mixed $pageSize Số lượng record tối đa trả về
	 * @return array Kêt quả là 1 hay nhiều ID để lấy kết quả từ table chứa dữ liệu thực thế+ percent matching
	 * @author ThanhViet [20110121]
	 */
	public function search_for_keyword($strKey,$numPage,$pageSize,$m_strFilter)
	{
		$strKey = trim($strKey);
		//$startTime =user_common::getMicrotime();
		//kết quả trả về là 100%
		$isExactly = false;
		//khởi tạo các thông số để connect đến sphinx server
		$cl = new SphinxClient();
		//$cl->SetServer( 'localhost', 3312 );	
		//$cl->SetServer( '192.168.1.114', 9306 );	
		$cl->SetServer( self::SPHINX_HOST_NAME, self::SPHINX_PORT_NUMBER );		
		$arrDeterminer= array(',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
		
		//nếu không phải có dấu search chính xác thì loại bỏ các ký tự ghép và ký tự lạ
		if (strpos($strKey,'"')!=0 || strrpos($strKey,'"') != strlen($strKey)-1 )
		{
			$strKey = str_replace($arrDeterminer,'',$strKey) ;
		}
		
		$arrOperator= user_common::explodeX(array('&','+','|','-','!'),$strKey);
		if (count($arrOperator)>1) //có các toán tử search
		{
			//kết quả trả về là 100%
			$isExactly = true;
			//echo '<br>Boolean:<br>';
			$cl->SetMatchMode( SPH_MATCH_BOOLEAN);
		}
		else //không có toán tử search
		{
			if (strpos($strKey,'"')==0 && strrpos($strKey,'"') == strlen($strKey)-1 )
			{
				//kết quả trả về là 100%
				$isExactly = true;
				//search chính xác nguyên câu
				//echo '<br>Phrase:<br>';
				$cl->SetMatchMode( SPH_MATCH_PHRASE);
			}
			else
			{
				//search bất kỳ từng chữ
				//echo '<br>Any:<br>';
				$cl->SetMatchMode( SPH_MATCH_ANY);
			}
			
		}
		
		//giới hạn và phân trang kết quả search
		$cl->SetLimits( $numPage, $pageSize, $this->_iMaxRecord );
		$cl->SetArrayResult(true);
		
		//kiem tra field sort
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		$strAttrib .= '';
		$strFilter .= '';
		$arrWeight = array();
		//thực hiện lấy field và dữ liệu cần filter
		foreach ($arrField as $field)
		{
			if($field['attribute']['order'])
			{
				$strAttrib .=$field['attribute']['name'].' '.$field['attribute']['order'].',';
			}
			//khong gan hard code nua. Ma truyen vao dong
			if($field['attribute']['filter'])
			{
				//$strFilter .=$field['attribute']['name'].' '.$field['attribute']['filter'].',';
			}
			if($field['attribute']['weight'] > 1)
			{
				//echo  $field['attribute']['name'].''=> '.$field['attribute']['weight'];
				$arrWeight = array_merge($arrWeight,array($field['attribute']['name'] => $field['attribute']['weight'])) ;
			}
		}
		
		//echo '<br>Arrweight:<br>';
		//print_r($arrWeight);
		if (count($arrWeight)> 0) //có set weight theo field
		{
			$cl->SetFieldWeights($arrWeight);
		}
		//echo $strAttrib;
		//nếu co field can sort
		if ($strAttrib) 
		{
			$strAttrib = user_common::cutLast($strAttrib,1);
			$strAttrib = '@weight DESC ,'.$strAttrib;
			$cl->SetSortMode(SPH_SORT_EXTENDED , $strAttrib );
		}
		$strFilter=$m_strFilter;
		
		//$catalist = array(80);
//$cl->SetFilter ( "content_id", $catalist);
		
		//echo $strFilter.'<br>';
		//$strFilter = 'content_id 81,';
		if($strFilter)//có field cần filter lại
		{
			$cl->SetSelect ( '*, '.$strFilter.' AS mycond' );
			$cl->SetFilter ( "mycond", array(1) );
			//$strFilter = user_common::cutLast($strFilter,1);
			/*$arrFilter = explode(',',$strFilter);
			foreach($arrFilter as $item)
			{
				$arrItem = explode(' ',$item);
				///print_r($arrItem);
				//echo $arrItem[1]." : ";
				//print_r(explode('and',$arrItem[1]));
				$cl->SetFilter( $arrItem[0], explode('and',$arrItem[1]) );
			}*/
		}
		
		
		user_common::writeLog( 'Search for keyword with index: dis_'.$this->_indexName,0,$_mainFrame->_pName);
		
		$result = $cl->Query($strKey, 'dis_'.$this->_indexName);
		//$result = $cl->Query($strKey, 'index_t_property_index_1');
		//print_r($result);
		//echo "<br>";
		$arrResult = array();
		$listID='';
		$listWeight='';
		if ($result ==false) 
		{
			//echo 'Query failed: ' . $cl->GetLastError() . '.\n';
			user_common::writeLog('Query failed function search of index '.$this->_indexName.' with keyword :'.$strKey.': '.$cl->GetLastError(),0,$_mainFrame->_pName);
		}
		else 
		{
			if ( $cl->GetLastWarning() ) 
			{
				//echo 'WARNING: ' . $cl->GetLastWarning();
				user_common::writeLog('WARNING function search of index '.$this->_indexName.': '.$cl->GetLastWarning(),1,$_mainFrame->_pName);
			}
			if ( ! empty($result['matches']) ) 
			{
				//lấy danh sách ID tướng ứng với weight
				foreach ( $result['matches'] as $doc  ) 
				{
					//echo '$doc\n,';
					$listID .=$doc['id'].',';
					$listWeight .=$doc['weight'].',';
					//phân bố các ID theo weight tương ứng
					$arrResult[$doc['weight']] .= $doc['id'].',';
				}
				//print_r( $result );
			}
		}
		$listID= substr($listID,0,strlen($listID)-1); 
		
		$listWeight= substr($listWeight,0,strlen($listWeight)-1); 
		
		$arrID = explode(',',$listID);
		$arrWeight = explode(',',$listWeight);
		//echo '<br>Weight:<br>';
		//print_r($arrWeight);
		//echo '<br>ArrID:<br>';
		//print_r($arrID);
		//echo '<br>Result:<br>';
		//print_r($arrResult);
		
		//$endTime =user_common::getMicrotime();
		//$time = $endTime - $startTime;
		
		//echo '<br>Total takes time $time seconds\n';
		//return;
		//lấy các field cần select của table index
		$strSelectField =$this->getSelectField();
		//echo '<br>Select Field:'.$strSelectField;
		//end
		$index=0;
		$arrWeight = array_keys($arrResult);
		$arrDocumentID =  Array();
		$arrWeightID =  Array();
		$arrKey = user_common::explodeX(array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*'),$strKey);
		$countKey = count($arrKey);
		//loại bỏ các word trùng nhau để vì weight thấp khi keyword có từ trùng
		$totalWord = $countKey;
		for($i=0;$i<$countKey-1;$i++)
		{
			for($j=$i+1;$j<$countKey;$j++)
			{
				//những tư singular xem như plural
				if (user_common::pluralToSingular(strtolower($arrKey[$i])) == user_common::pluralToSingular(strtolower($arrKey[$j])) ) 
				{
					$totalWord -= 1.38;
				}	
			}
		}
		//echo '<br>$totalWord: '.$totalWord;
		$maxWeight = ($totalWord*2-1)*$totalWord;
		//echo '<br>Weight: '.$arrWeight[$index];
		//echo '<br>Weight Max: '.$maxWeight;
		//echo '<br>Percent Match: '.(($arrWeight[$index]/$maxWeight)*100).'%';
		//chuyển các chuỗi ID thành các mảng chứa ID đã sắp xếp theo weight
		foreach($arrWeight as $weight)
		{
			$arrResult[$weight] = user_common::cutLast($arrResult[$weight],1);
			$arrID = explode(',',$arrResult[$weight]);
			foreach($arrID as $ID)
			{
				$arrDocumentID[$ID]['id']=$ID;
				//nếu search chính xác thì kết quả luôn là 100%
				if ($isExactly==true) 
				{
					$arrDocumentID[$ID]['matching']=100;
				}
				else
				{
					$arrDocumentID[$ID]['matching']=($weight/$maxWeight)*100;
					if($arrDocumentID[$ID]['matching']>100)
					{
						$arrDocumentID[$ID]['matching']=100;
					}
				}	
			}
		}
		//echo '<br>';
		//print_r($arrDocumentID);
		//return;
		$arrTableID =  Array();
		//đưa các ID vào các mảng chứa table index tương ứng
		foreach($arrDocumentID as $ID)
		{
			if($ID['id'] % self::MAX_NUM_RECORD_FOR_TABLE ==0)
			{
				$numTable = $ID['id']/self::MAX_NUM_RECORD_FOR_TABLE;				
			}
			else
			{
				$numTable = floor($ID['id']/self::MAX_NUM_RECORD_FOR_TABLE) +1;				
			}
			$arrTableID[$numTable] .='\''.$ID['id'].'\',';
		}
		//echo '<br>ArrTable: ';
		//print_r($arrTableID);
		
		//return;
		
		$strSQL='';
		//lấy các table index
		$arrKey =  array_keys($arrTableID);
		$count  = count($arrKey);
		//echo '<br>count: '.$count;
		//tạo câu SQL select các record tương ứng với table index
		for($i=0;$i<$count;$i++)
		{
			//echo '<br>count i: '.$i;
			$strTableName = $this->_indexName.self::SUFFIX_TABLE_INDEX.($arrKey[$i]);
			$arrTableID[$arrKey[$i]] = user_common::cutLast($arrTableID[$arrKey[$i]],1);
			$strSQL .= user_common::prepareQuery(user_common::SQL_SELECT_FREE,array('id,doc_id',$strTableName,'where id IN('.$arrTableID[$arrKey[$i]].')')).';';
		}
		//echo '<br/>SQL : '.$strSQL;
		//return;
		$result = $this->_objConnection->selectMultiCommand($strSQL);
		//echo '<br>Result: ';
		//print_r($result);
		
		//return;
		//chuyển chỉ mục
		//unset($arrDocumentID);
		
		foreach($result as $item)
		{
			foreach($item as $subItem)
			{
				$arrDocumentID[$subItem['id']]['id']=$subItem['doc_id'];
			}
		}
		//echo '<br>Document ID: ';
		//print_r($arrDocumentID);
		//tre ve  index va doc_id
		foreach($arrDocumentID as $item)
		{
			$arrResultID[$item['id']]=$item;
		}
		//echo '<br>Resul Document ID: ';
		//print_r($arrResultID);
		return $arrResultID;
		
		/*
		$arrResult = array();
		$arrIndex  = array();
		//echo '<br>Tổng cộng có '.count($arrDocumentID).' result:<br>';
		//return;
		
		foreach($arrDocumentID as $document)
		{
			//tính toán lại đoạn trùng thật sự có xử lý unicode
			$arrOutput= $this->highlightResult($document['vn_content'],$strKey);
			$count = $arrOutput['end']-$arrOutput['start'];
			if (!array_key_exists($count, $arrResult))
			{
				$arrResult[$count]= array();
				$arrIndex[$count] = 0;
			}
			//sắp xếp lại các document sau khi đã tính toán lại đoạn trùng
			$arrResult[$count][$arrIndex[$count]++] = $document;
			
		}
		//empty($arrDocumentID);
		unset($arrID);
		foreach($arrResult as $result)
		{
			
			foreach($result as $document)
			{
				//print_r($document);
				$arrID[$document['doc_id']]=$document['doc_id'];
			}
		}
		
		return $arrID;
		*/
		/*
		$arrKey = user_common::explodeX(array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*'),$strKey);
				
		foreach($arrResult as $result)
		{
			
			foreach($result as $document)
			{
				//output ra các field cần output
				foreach ($arrField as $field)
				{
					
					//echo '<br>'.$field['attribute']['name'].': ';
					$arrOutput= $this->highlightResult($document[$field['attribute']['name']],$strKey);
					//print_r($arrOutput);
					
					//echo $arrOutput['content'];
					$arrStr = user_common::explodeX(array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*'),$arrOutput['content']);
					$strOutput='';
					
					//xuất đoạn bref trùng khớp với keyword
					for	($i=$arrOutput['start']-self::DECREMENT_START_BREF ;$i <$arrOutput['end']+self::INCREMENT_END_BREF;$i++ )
					{
						$strOutput .= $arrStr[$i] .' ';
					}
					
					//$strOutput .='...';
					//$strOutput = substr($arrOutput['content'],$arrOutput['start'],$arrOutput['end']-$arrOutput['start']+40);
					
					//hightlight bref output
					$arrOutput =  $this->highlightResult(strip_tags($strOutput),$strKey);
					//echo '<br>Bref output: '.$arrOutput['content'];//.'<br>';
					//echo '<br>Bref output: '.$strOutput;//.'<br>';
					
					$maxWeight = (count($arrKey)*2-1)*count($arrKey);
					//echo '<br>Weight: '.$arrWeight[$index];
					//echo '<br>Weight Max: '.$maxWeight;
					//echo '<br>Percent Match: '.(($arrWeight[$index]/$maxWeight)*100).'%';				
					$index ++;
				}
			}
		}
		*/
		
		
	}
	
	/**
	 * Phương thức search với Sphinx
	 *
	 * @param string $strKey Chuỗi cần search
	 * @param mixed $numPage Số thứ tụ trang hiện tại
	 * @param mixed $pageSize Số lượng record tối đa trả về
	 * @return array Kêt quả là 1 hay nhiều ID để lấy kết quả từ table chứa dữ liệu thực tế
	 * @author ThanhViet [20110121]
	 */
	public function search($strKey,$numPage,$pageSize)
	{
		user_common::writeLog('Start search ',1,$_mainFrame->pPage);
		$strKey = trim($strKey);
		//tính toàn thời gian search
		//$startTime =user_common::getMicrotime();
		
		//khởi tạo các thông số để connect đến sphinx server
		$cl = new SphinxClient();
		//$cl->SetServer( 'localhost', 3312 );	
		//$cl->SetServer( '192.168.1.114', 9306 );	
		$cl->SetServer( self::SPHINX_HOST_NAME, self::SPHINX_PORT_NUMBER );		
		//$cl->SetMatchMode( SPH_MATCH_ALL);
		$arrDeterminer= array(',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
		//nếu không phải có dấu search chính xác thì loại bỏ các ký tự ghép và ký tự lạ
		if (strpos($strKey,'"')!=0 || strrpos($strKey,'"') != strlen($strKey)-1 )
		{
			$strKey = str_replace($arrDeterminer,'',$strKey) ;
		}
		
		$arrOperator= user_common::explodeX(array('&','+','|','-','!'),$strKey);
		if (count($arrOperator)>1) //có các toán tử search
		{
			//echo '<br>Boolean:<br>';
			$cl->SetMatchMode( SPH_MATCH_BOOLEAN);
		}
		else //không có toán tử search
		{
			//strpos($strKey,'"');
			if (strpos($strKey,'"')==0 && strrpos($strKey,'"') == strlen($strKey)-1 )
			{
				//search chính xác nguyên câu
				//echo '<br>Phrase:<br>';
				$cl->SetMatchMode( SPH_MATCH_PHRASE);
			}
			else
			{
				//search bất kỳ từng chữ
				//echo '<br>Any:<br>';
				$cl->SetMatchMode( SPH_MATCH_ANY);
			}
			
		}
		//$cl->SetMatchMode( SPH_RANK_SPH04  );
		//giới hạn và phân trang kết quả search
		$cl->SetLimits( $numPage, $pageSize, $this->_iMaxRecord );
		$cl->SetArrayResult(true);
		//$cl->SetGroupBy('vn_doc_len', SPH_GROUPBY_ATTR, 'vn_doc_len ASC' );
		
		//kiem tra field sort
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		$strAttrib .= '';
		$strFilter .= '';
		$arrWeight = array();
		//thực hiện lấy field và dữ liệu cần filter
		foreach ($arrField as $field)
		{
			if($field['attribute']['order'])
			{
				$strAttrib .=$field['attribute']['name'].' '.$field['attribute']['order'].',';
			}
			if($field['attribute']['filter'])
			{
				$strFilter .=$field['attribute']['name'].' '.$field['attribute']['filter'].',';
			}
			if($field['attribute']['weight'] > 1)
			{
				//echo  $field['attribute']['name'].'\'=> '.$field['attribute']['weight'];
				$arrWeight = array_merge($arrWeight,array($field['attribute']['name'] => $field['attribute']['weight'])) ;
			}
		}
		//echo '<br>Arrweight:<br>';
		//print_r($arrWeight);
		if (count($arrWeight)> 0) //có set weight theo field
		{
			$cl->SetFieldWeights($arrWeight);
		}
		//nếu co field can sort
		if ($strAttrib) 
		{
			$strAttrib = user_common::cutLast($strAttrib,1);
			$strAttrib = '@weight DESC ,'.$strAttrib;
			$cl->SetSortMode(SPH_SORT_EXTENDED , $strAttrib );
		}
		if($strFilter)//có field cần filter lại
		{
			$strFilter = user_common::cutLast($strFilter,1);
			$arrFilter = explode(',',$strFilter);
			foreach($arrFilter as $item)
			{
				$arrItem = explode(' ',$item);
				//print_r($arrItem);
				$cl->SetFilter( $arrItem[0], array( $arrItem[1] ) );
			}
		}
		//$result = $cl->Query($strKey, 'dis_'.$this->_indexName);
		$result = $cl->Query($strKey, 'index_t_faq_index_1');
		
		//print_r($result);
		
		$arrResult = array();
		$listID='';
		$listWeight='';
		if ( $result === false ) 
		{
			//echo 'Query failed: ' . $cl->GetLastError() . '.\n';
			user_common::writeLog('Query failed function search of index '.$this->_indexName.' with keyword :'.$strKey.': '.$cl->GetLastError(),0,$_mainFrame->_pName);
			
		}
		else 
		{
			if ( $cl->GetLastWarning() ) 
			{
				//echo 'WARNING: ' . $cl->GetLastWarning();
				user_common::writeLog('WARNING function search of index '.$this->_indexName.': '.$cl->GetLastWarning(),0,$_mainFrame->_pName);
			}
			if ( ! empty($result['matches']) ) 
			{
				//lấy danh sách ID tướng ứng với weight
				foreach ( $result['matches'] as $doc  ) 
				{
					//$listID .=$doc['id'].',';
					//$listWeight .=$doc['weight'].',';
					//phân bố các ID theo weight tương ứng
					$arrResult[$doc['weight']] .= $doc['id'].',';
				}
			}
		}
		$listID= substr($listID,0,strlen($listID)-1); 
		
		//$endTime =user_common::getMicrotime();
		//$time = $endTime - $startTime;
		
		//echo '<br>Total takes time $time seconds\n';
		//return;
		//lấy các field cần select của table index
		$strSelectField =$this->getSelectField();
		//end
		$index=0;
		
		$arrDocumentID =  Array();
		//chuyển các chuỗi ID thành các mảng chứa ID đã sắp xếp theo weight
		foreach($arrResult as $strResult)
		{
			$strResult = user_common::cutLast($strResult,1);
			$arrID = explode(',',$strResult);
			
			foreach($arrID as $ID)
			{
				$arrDocumentID[$ID]=$ID;
			}
		}
		
		//print_r($arrDocumentID);
		//return;
		$arrTableID =  Array();
		//đưa các ID vào các mảng chứa table index tương ứng
		foreach($arrDocumentID as $ID)
		{
			if($ID % self::MAX_NUM_RECORD_FOR_TABLE ==0)
			{
				$numTable = $ID/self::MAX_NUM_RECORD_FOR_TABLE;				
			}
			else
			{
				$numTable = floor($ID/self::MAX_NUM_RECORD_FOR_TABLE) +1;				
			}
			$arrTableID[$numTable] .='\''.$ID.'\',';
		}
		//echo '<br>ArrTable: ';
		//print_r($arrTableID);
		
		
		
		//only for testing
		
		$strSQL='';
		//lấy các table index
		$arrKey =  array_keys($arrTableID);
		$count  = count($arrKey);
		//echo '<br>count: '.$count;
		//tạo câu SQL select các record tương ứng với table index
		for($i=0;$i<$count;$i++)
		{
			//TODO sua lai thanh table index
			//echo '<br>count i: '.$i;
			$strTableName = $this->_indexName.self::SUFFIX_TABLE_INDEX.($arrKey[$i]);
			$arrTableID[$arrKey[$i]] = user_common::cutLast($arrTableID[$arrKey[$i]],1);
			$strSQL .= user_common::prepareQuery(user_common::SQL_SELECT_FREE,array($strSelectField,$strTableName,'where id IN('.$arrTableID[$arrKey[$i]].')')).';';
		}
		
		$result = $this->_objConnection->selectMultiCommand($strSQL);
		if(!$result)
		{
			user_common::writeLog('Function search fail select data '.$strSQL,1,$_mainFrame->_pName);
		}
		//unset($arrDocumentID);
		foreach($result as $item)
		{
			foreach($item as $subItem)
			{
				$arrDocumentID[$subItem['id']]=$subItem;
			}
		}
		//echo '<br>result:<br>';
		//print_r($arrDocumentID);
		//echo '<br>';
		
		return $arrDocumentID;
		
		//hiển dữ liệu chạy test
		/*
		$arrResult = array();
		$arrIndex  = array();
		echo '<br>Tổng cộng có '.count($arrDocumentID).' result:<br>';
		//return;
		
		foreach($arrDocumentID as $document)
		{
			//print_r($arrField);
			foreach ($arrField as $field)
			{
				//echo '<br>'.$field['attribute']['name'];
				//echo '<br>'.$field['attribute']['name'].': ';
				$arrOutput= $this->highlightResult($document[$field['attribute']['name']],$strKey);
				
				echo '<br>'.$field['attribute']['name'].'->'. $arrOutput['content'];
				//$arrStr = user_common::explodeX(array(' ',',','?','.',';',':','\'','"','-','=','!','(',')','[',']','{','}','|','+','\\','*'),$arrOutput['content']);
				//$strOutput='';
				
				//for	($i=$arrOutput['start']-self::DECREMENT_START_BREF ;$i <$arrOutput['end']+self::INCREMENT_END_BREF;$i++ )
				//{
				//	$strOutput .= $arrStr[$i] .' ';
				//}
				
				//$strOutput .='...';
				//$strOutput = substr($arrOutput['content'],$arrOutput['start'],$arrOutput['end']-$arrOutput['start']+40);
				//$arrOutput =  $this->highlightResult(strip_tags($strOutput),$strKey);
				//echo '<br>Bref output: '.$arrOutput['content'];//.'<br>';
				//echo '<br>Bref output: '.$strOutput;//.'<br>';
				
				//$maxWeight = (count($arrKey)*2-1)*count($arrKey);
				// 				echo '<br>Weight: '.$arrWeight[$index];
				// 				echo '<br>Weight Max: '.$maxWeight;
				// 				echo '<br>Percent Match: '.(($arrWeight[$index]/$maxWeight)*100).'%';				
				//$index ++;
			}
		}
		*/
		
	}
	
	/**
	 * Thực thi các chức năng reindex, refresh data, reindex meta khi có crontab chạy
	 *
	 * @return void
	 * @author ThanhViet [20110121]
	 */
	public function runCronTab() 
	{
		//tính toán số lượng phút giữa ngay hiện tại và ngày lấy mốc
		$totalMinutes = user_common::timeDifference(self::MILESTONE_TIME,'NOW','minutes');
		//reindex
		if($this->_reindexTime)
		{
			echo '<br/><b>Reindex Main</b>';
			//tính toán số lượng phút với so với thời gian cấu hình reindex
			
			$intervalMinutes = user_common::calculateDateTime($this->_reindexTime,'minutes');
			echo '<br>Interval: '.$this->_reindexTime;
			
			//tính toán số phút chia dư để thực hiện remeta index
			$differenceTime = $totalMinutes % ($intervalMinutes);
			
			echo '<br>Reindex Main interval: '.$intervalMinutes .' total: '.$totalMinutes.' differenceTime: '.$differenceTime;
			
			if($differenceTime >0 && $differenceTime <=self::INTERVAL_CRONTAB_RUN)//đã tới thời gian reindex
			{
				$this->buildMainIndex();
				echo '<br><b>Run build main index.......'.$this->_indexName.'</b>';
				user_common::writeLog('Run build main index.......'.$this->_indexName.'with Tnterval: '.$intervalMinutes .' Total: '.$totalMinutes.' DifferenceTime: '.$differenceTime,1,$_mainFrame->_pName);
			}
		}
		//refresh data
		if($this->_refreshTime)
		{
			echo '<br/><b>Refresh Data</b>';
			//tính toán số lượng phút với so với thời gian cấu hình refresh time
			$intervalMinutes = user_common::calculateDateTime($this->_refreshTime,'minutes');
			echo '<br>Interval: '.$this->_refreshTime;
			
			//tính toán số phút chia dư để thực hiện remeta index
			$differenceTime = $totalMinutes % ($intervalMinutes);
			
			echo '<br>Refresh Data interval: '.$intervalMinutes .' total: '.$totalMinutes.' differenceTime: '.$differenceTime;
			
			if($differenceTime >0 && $differenceTime <self::INTERVAL_CRONTAB_RUN)//đã tới thời gian refresh data
			{
				//chuyển dữ liệu từ table thực tế sang table index
				$this->refeshDataFromBackupTable();	
				$this->buildMainIndex();
				echo '<br><b>Run refresh data.......'.$this->_indexName.'</b>';
				user_common::writeLog('Run refresh data.......'.$this->_indexName.'with Interval: '.$intervalMinutes .' Total: '.$totalMinutes.' DifferenceTime: '.$differenceTime,1,$_mainFrame->_pName);
			}
		}
		//reindex meta
		if($this->_reindexMetaTime)
		{
			echo '<br/><b>Reindex Meta</b>';
			
			//tính toán số lượng phút với so với thời gian cấu hình reindex meta
			$intervalMinutes = user_common::calculateDateTime($this->_reindexMetaTime,'minutes');
			echo '<br>Interval: '. $this->_reindexMetaTime;
			
			//tính toán số phút chia dư để thực hiện remeta index
			$differenceTime = $totalMinutes % ($intervalMinutes);
			
			echo '<br>Reindex Meta interval: '.$intervalMinutes .' Total: '.$totalMinutes.' DifferenceTime: '.$differenceTime;
			if($differenceTime >0 && $differenceTime <=self::INTERVAL_CRONTAB_RUN)//đã tới thời gian reindex meta
			{
				//build meta index
				$this->buildMetaIndex();
				echo '<br><b>Run reindex meta.......'.$this->_indexName.'</b>';
				user_common::writeLog('Run reindex meta.......'.$this->_indexName.'with Interval: '.$intervalMinutes .' Total: '.$totalMinutes.' DifferenceTime: '.$differenceTime,1,$_mainFrame->_pName);
			}
		}
	}
	
	#end region
	
	#region Private methods
	/**
	 * Phương thức chuyển dữ liệu từ file config XML.
	 *
	 * @return void 
	 * @author ThanhViet [20110121]
	 */
	function parseFileConfigXML()
	{
		//chuyển file XML thành array
		//nếu file cấu hình không tồn tại thì thoát
		if (!file_exists(self::FOLDER_FILES_SPHINX.$this->_indexName.Application::XML_EXTENSION)) {
			return;
		}
		$this->_arrXML = Application::getVarXML(self::FOLDER_FILES_SPHINX.$this->_indexName,'sphinx');
		
		//$this->_prefixTableIndex = 
		$arrPara =$this->_arrXML['children']['para'];
		
		//đọc các param lưu vào biến toàn cục
		foreach ($arrPara as $item)
		{
			if (isset($item['attribute']['block_item'])) 
			{
				$this->_iBLock = $item['attribute']['block_item'];
			} 
			if (isset($item['attribute']['config_file'])) 
			{
				$this->_fileSphinx = $item['attribute']['config_file'];
			} 
			if (isset($item['attribute']['max_record'])) 
			{
				$this->_iMaxRecord = $item['attribute']['max_record'];
			} 
			if (isset($item['attribute']['reindex_time'])) 
			{
				$this->_reindexTime = $item['attribute']['reindex_time'];
			} 
			if (isset($item['attribute']['refresh_time'])) 
			{
				$this->_refreshTime = $item['attribute']['refresh_time'];
			} 
			if (isset($item['attribute']['reindex_meta_time'])) 
			{
				$this->_reindexMetaTime = $item['attribute']['reindex_meta_time'];
			} 
		}
	}
	
	/**
	 * Build chuỗi SQl để tạo table index
	 *
	 * @return string Chuỗi SQl create index
	 *
	 */
	function buildCreatTableSQL() 
	{
		$strCreateTable = 'CREATE TABLE `{0}` (   
				`id` bigint(20) NOT NULL AUTO_INCREMENT,';
		//danh sách các field
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		
		//tổng hợp các field cần create table
		foreach ($arrField as $item)
		{
			//lấy field name
			$strCreateTable .= '`'.$item['attribute']['name'].'` ';
			//lấy data type
			$strCreateTable .= $item['attribute']['datatype'];
			if (strrpos($item['attribute']['datatype'],'har') == false) //nếu không là kiểu chuỗi
			{
				$strCreateTable .=',';
			}
			else //nếu là kiểu chuỗi
			{
				$strCreateTable .= '('.$item['attribute']['length'].'),';
				
			}
		}           
		$strCreateTable .='PRIMARY KEY (`id`)
				) ENGINE=MyISAM  AUTO_INCREMENT={1} DEFAULT CHARSET=utf8;';
		return $strCreateTable;
	}
	
	/**
	 * Xay dựng câu sql insert vào table
	 *
	 * @return string cau sql insert
	 *
	 */
	function buildInsertSQL()
	{
		$strSQL = 'insert into {0}(';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		
		//tổng hợp các field cần insert
		foreach ($arrField as $item)
		{
			$strSQL .='`'.$item['attribute']['name'].'`,';
		}
		
		$strSQL = user_common::cutLast($strSQL,1);
		$strSQL .= ') values(';
		$i = 1;
		
		foreach ($arrField as $item)
		{
			$strSQL .='\'{'.($i++).'}\',';
		}
		$strSQL = user_common::cutLast($strSQL,1);
		$strSQL .= ')';
		return $strSQL;
	}
	
	/**
	 * Lấy tất cả table index 
	 * 
	 * @param string $strSuffixTable Suffix của table cần lấy 
	 * 
	 * @return array Danh sách table index
	 *
	 */
	private function getAllTableIndex($strSuffixTable) 
	{
		//SQL lấy tất cả tablename của table index
		$strSQL = user_common::prepareQuery(user_common::SQL_SELECT_TABLE_NAME,array($this->_indexName.$strSuffixTable.'%'));
		//echo '<br>SQL: '.$strSQL;
		$arrTable = $this->_objConnection->selectCommand($strSQL);
		if (!$arrTable)
		{
			user_common::writeLog('getAllTableIndex:'.$strSQL,1,$_mainFrame->_pName);
			return false;
		}
		return $arrTable;	
	}
	/**
	 * Lấy số index của table
	 *
	 * @param string $strTableName Tên table
	 * @return int Số index
	 * @author ThanhViet [20110121]
	 */
	private function getNumofTable($strTableName) 
	{
		return substr($strTableName,strrpos($strTableName,'_')+1);
	}
	
	/**
	 * Lấy các field cần select của table index.$item. Điều kiện isOutput=1
	 *
	 * @return string Chuỗi SQL select field
	 * @@author ThanhViet[20110211]
	 *
	 */
	private function getSelectField() 
	{
		//lấy các field cần select của table index
		$strSelectField ='';
		$arrField = $this->_arrXML['children']['table'][0]['children']['field'];
		$strSelectField .= '`id`,';
		foreach ($arrField as $field)
		{
			if($field['attribute']['isOutput']=='1')
			{
				$strSelectField .='`'.$field['attribute']['name'].'`,';
			}
		}
		$strSelectField = user_common::cutLast($strSelectField,1);
		//echo '<br>Select Field:'.$strSelectField.'<br>';
		return $strSelectField;
	}
	
	
	/**
	 * Highlight chuỗi
	 *
	 * @param string $haystack Nội dung cần search
	 * @param string $keyword Từ khóa để search
	 * @param int $showALL 1-> hiển thị hết phần bold
	 * @return mixed This is the return value description
	 *
	 */
	function highlight_match($haystack,$keyword,$showALL=0)
   {
		//echo'<br>Bold->';
		//bold đoạn trùng
		$haystack=self::boldMatching($haystack,$keyword);
		//echo $haystack;
		if($showALL==1)
		{
			return $haystack;
		}
		//encoding để substr cho chính xác
		$encoding='UTF-8';
		$countWordMax=0;//luu so tu match nhieu nhat
		$search='<b>';
		$len_sch=mb_strlen('<b>');
		//$len_rep=mb_strlen($replace,$encoding);
		//echo '<br>';
		$offset=0;
		$iStartMax=0;
		//$countInput = strlen($haystack);
		$countInput = mb_strlen($haystack,$encoding);
		//lấy tối thiểu
		$iEndMax=$countInput>400?400:$countInput;
		while (($offset=mb_strpos($haystack,$search,$offset))!==false)
		{
			//echo '<br>';
			//echo 'offset 1->'.$offset.'<br>';
			$search2 ='</b>';
			$len_rep=mb_strlen('</b>');
			$offset2= mb_strpos($haystack,$search2,$offset);
			//echo 'offset 2->'.$offset2.'<br>';
			//$len_rep = mb_strlen('<b>'.mb_substr($haystack,$offset,$len_sch,$encoding).'</b>');
			//echo '<br>'.mb_substr($haystack,$offset,$offset2-$offset+$len_rep).'<br>';
			//echo '<br>Word count->'.count_words(mb_substr($haystack,$offset,$offset2-$offset)) ;
			$newCount=self::count_words(mb_substr($haystack,$offset,$offset2-$offset,$encoding));
			if ($newCount>$countWordMax) {
				$countWordMax=$newCount;
				$iStartMax = $offset;
				$iEndMax = $offset2;
			}
			
			$offset=$offset2;
			if ($offset>mb_strlen($haystack))break;
			
		}
		//echo $iStartMax;
		//echo '<br>count->'.$countInput;
		//echo '<br>Start match->'.$iStartMax.':'.$iEndMax;
		//lấy từ đoạn trùng nhiều nhất trở về trước, Dùng khi gặp dấu câu hay quá 300 kí tự và xong một chữ
		for($i=$iStartMax;$i>=0;$i--)
		{
			$subTemp = mb_substr($haystack,$i,1,$encoding);
			//echo '<br>'.$haystack[$i].'<br>';
			if (((($iStartMax-$i)>300)&& ($subTemp == ' '||$subTemp == ','||$subTemp == ';')) || (($subTemp == '.'||$subTemp == '?'||$subTemp == '!'))) 
			{
				break;
			}	
		}		
		$startOffset = $i+1;
		
		//lấy cuối câu trừ đoạn trùng
		for($i = $iStartMax+1 ;$i< $countInput;$i++)
		{
			$subTemp = mb_substr($haystack,$i,1,$encoding);
			//echo '<br>char:'.$i.':'.$subTemp.'<br>';
			//lấy luôn dấu cuối câu.
			//phải lấy qua đoạn bold.Nếu cần lấy hết không quan tâm dấu chấm câu
			if ( ((($i - $iStartMax)>300)&&($subTemp == ' '||$subTemp == ','||$subTemp== ';')) || ($i > $iEndMax && ($i - $iStartMax >200) &&  ($subTemp == '.'||$subTemp == '?'||$subTemp == '!')) ) 
			{
				break;
			}	
		}
		//lấy luôn dấu chấm câu
		//$i=$i+1;
		if ($i>=$countInput) 
		{
			$i=$countInput;
		}
		$endOffset = $i+1;
		//echo '<br>start->'.$startOffset.':'. $endOffset;
		//không substr theo utd=8 được vì startOffset và endOffset được lấy theo từng byte
		//thêm dấu đóng thẻ bold để ko bị trường hợp thiếu thẻ đóng
		return trim(mb_substr($haystack,$startOffset,$endOffset-$startOffset,$encoding)).'</b>';
	}
	
	
	/**
	 * Đếm số lượng trừ trong chuỗi
	 *
	 * @param string $string Chuỗi cần đếm
	 * @return int Số từ trong chuỗi
	 *
	 */
	function count_words($string)
	{
		$string = htmlspecialchars_decode(strip_tags($string));
		if (strlen($string)==0)
			return 0;
		$t = array(' '=>1, '_'=>1, "\x20"=>1, "\xA0"=>1, "\x0A"=>1, "\x0D"=>1, "\x09"=>1, "\x0B"=>1, "\x2E"=>1, "\t"=>1, '='=>1, '+'=>1, '-'=>1, '*'=>1, '/'=>1, '\\'=>1, ','=>1, '.'=>1, ';'=>1, ':'=>1, '"'=>1, '\''=>1, '['=>1, ']'=>1, '{'=>1, '}'=>1, '('=>1, ')'=>1, '<'=>1, '>'=>1, '&'=>1, '%'=>1, '$'=>1, '@'=>1, '#'=>1, '^'=>1, '!'=>1, '?'=>1); // separators
		$count= isset($t[$string[0]])? 0:1;
		if (strlen($string)==1)
			return $count;
		for ($i=1;$i<strlen($string);$i++)
			if (isset($t[$string[$i-1]]) && !isset($t[$string[$i]])) // if new word starts
				$count++;
		return $count;
	}
	
	
	/**
	 * Tô đậm phầm từ khóa search truyền vào
	 *
	 * @param string $haystack Nội dùn cần bold
	 * @param string $search Keyword search
	 * @return string Chuỗi đầu vào đã được bold
	 *
	 */
	function boldMatching($haystack, $search)
	{
		//echo '<br>String search:'.$search.'<br>';
		//tình toán len, hay pos trên bảng mã UTF-8 (mb_)
		$encoding='UTF-8';
		//bold,tìm kiếm từ đầu chuỗi input $haystack
		$offset=0;
		
		$arrDeterminer= array(' ',',','?','.',';',':','"','-','=','!','(',')','[',']','{','}','|','+','\\','*');
		$search = mb_strtolower($search,'UTF-8');
		
		//tách chuỗi search  ra làm từ từ riêng biệt
		$arrKey = user_common::explodeX($arrDeterminer,$search);
		
		//kiểm tra chuỗi phải unicode không để loại bỏ dấu khi bold
		$isUnicode = 0;
		if (user_common::checkUnicodeString($search) )
		{
			$isUnicode = 1;
		}
		mb_internal_encoding($encoding);
		mb_regex_encoding($encoding);
		foreach($arrKey as $key)
		{
			//echo '<br>Keyword->'.$key.'<br>';
			$len_sch=mb_strlen($key,$encoding);
			//$len_rep=mb_strlen($replace,$encoding);
			//nếu chỗi tìm không có dấu thì bold không phần biệt dấu
			if (!$isUnicode) 
			{
				$temp = user_common::utf8_to_ascii($haystack);
			}
			else
			{
				$temp=$haystack;
			}
			$temp = mb_strtolower($temp,'UTF-8');
			
			//echo mb_strpos($haystack,$search,$offset,$encoding).'-><br>';
			//
			while (($offset=mb_strpos($temp,$key,$offset,$encoding))!==false){
				if($offset>1)
				{
					$charBefore = mb_substr($haystack,$offset-1,1,$encoding);
				}
				$charAfter = mb_substr($haystack,$offset+$len_sch,1,$encoding);
				//echo '$charAfter->'.$charAfter;
				//loại bỏ không phải là ký tư
				$charBefore = preg_replace('/[^a-z0-9]+/i','',$charBefore); 
				$charAfter = preg_replace('/[^a-z0-9]+/i','',$charAfter); 
				//echo 'offset->'.$offset.'<br>';
				$len_rep = mb_strlen('<b>'.mb_substr($haystack,$offset,$len_sch,$encoding).'</b>');
				//nếu đoạn trùng không phải là một chữ thì tiếp tục đoạn khác
				if ($charBefore !='' || $charAfter !='') 
				{
					$offset=$offset+$len_rep;
					continue;
				}
				//đưa thẻ bold vào đoạn trùng
				$haystack=mb_substr($haystack,0,$offset,$encoding)
					//.$replace
					.'<b>'.mb_substr($haystack,$offset,$len_sch,$encoding).'</b>'
					.mb_substr($haystack,$offset+$len_sch);
				$offset=$offset+$len_rep;
				//đã tới cuối chuỗi thì thoát
				if ($offset>mb_strlen($haystack,$encoding))break;
				
				//gán lại chuỗi bao gồm đã bold trước đó
				if (!$isUnicode) 
				{
					$temp = user_common::utf8_to_ascii($haystack);
				}
				else
				{
					$temp=$haystack;
				}
				$temp = mb_strtolower($temp,'UTF-8');
			}
		}
		//echo'<BR>Haystack: '.$haystack;
		//loại bỏ các thẻ <b> kế nhau
		//return str_replace('</b> <b>',' ',$haystack);
		return mb_ereg_replace(@"</b\b[^>]*>(\W{0,5})<b>",' ',$haystack);
	}
	
	
	
	/**
	 * Láy ngôn ngữ tùy chọn trong option cùng với keyword nhập vào lang:en hoặc lang:vi
	 *
	 * @param string $strKeyword Keyword search gồm có cả option
	 * @return mixed This is the return value description
	 *
	 */
	function getOptionLang(&$strKeyword) 
	{
		
		$posStartLang= strrpos($strKeyword,'lang:');
		if (!$posStartLang || $posStartLang==-1) 
		{
			return null;
		}
		$posEndLang= strpos($keyWord,' ',$posStartLang);
		//echo substr($keyWord,$posStartLang,$posEndLang-$posStartLang>0?$posEndLang-$posStartLang:100);
		
		//lấy vị trí của chuỗi lang: và lấy hết option lang
		//nếu lang:xx là chuỗi cuối thì $posEndLang=0 do đó nếu hiệu $posEndLang-$posStartLang <=0 thì lấy chuỗi dài 100 ký tự
		$strOptionLang = substr($strKeyword,$posStartLang,$posEndLang-$posStartLang>0?$posEndLang-$posStartLang:100);
		$strKeyword= str_replace($strOptionLang,'',$strKeyword);
		//tách ngôn ngữ
		$arrOptionLang = explode(':',$strOptionLang);
		//print_r($arrOptionLang);
		$lang = $arrOptionLang[1]=='en'?'en':'vi';
		return $lang;
	}
	
	#end region
	
}
?>